Instead of making system or portable file readers responsible for
authorBen Pfaff <blp@gnu.org>
Mon, 15 Nov 2004 09:51:22 +0000 (09:51 +0000)
committerBen Pfaff <blp@gnu.org>
Mon, 15 Nov 2004 09:51:22 +0000 (09:51 +0000)
dropping and reordering variables, make them read full cases and
let the caller take care of any changes.

Instead of providing a system or portable file writer with a raw
case in the format needed for output, provide it with a regular
case.  The writer takes care of any needed translation.

Instead of treating `struct file_handle' as a class to subclass
into data files, system files, and portable files, instead use it
as a helper that coordinates access.  Now it is opaque, too.

Break dictionary functions into separate header file.

Get rid of procedure-specific union in struct variable, using
instead a void * pointer and a destructor function.

86 files changed:
po/en_GB.po
po/pspp.pot
src/.cvsignore
src/ChangeLog
src/Makefile.am
src/aggregate.c
src/alloc.c
src/apply-dict.c
src/autorecode.c
src/barchart.c
src/cartesian.c
src/case.c
src/case.h
src/casefile.c
src/chart.c
src/chart.h
src/command.c
src/compute.c
src/correlations.q
src/count.c
src/crosstabs.q
src/data-list.c
src/descript.c
src/dfm-read.c [new file with mode: 0644]
src/dfm-read.h [new file with mode: 0644]
src/dfm-write.c [new file with mode: 0644]
src/dfm-write.h [new file with mode: 0644]
src/dfm.c [deleted file]
src/dfm.h [deleted file]
src/dictionary.c
src/dictionary.h [new file with mode: 0644]
src/examine.q
src/expr-evl.c
src/expr-prs.c
src/file-handle.h
src/file-handle.q
src/file-type.c
src/flip.c
src/frequencies.q
src/get.c
src/glob.c
src/group.c
src/group_proc.h
src/histogram.c
src/inpt-pgm.c
src/levene.c
src/list.q
src/loop.c
src/main.c
src/matrix-data.c
src/means.q
src/modify-vars.c
src/numeric.c
src/oneway.q
src/pfm-read.c
src/pfm-read.h [new file with mode: 0644]
src/pfm-write.c
src/pfm-write.h [new file with mode: 0644]
src/pfm.h [deleted file]
src/piechart.c
src/print.c
src/q2c.c
src/recode.c
src/rename-vars.c
src/repeat.c
src/sel-if.c
src/sfm-read.c
src/sfm-read.h [new file with mode: 0644]
src/sfm-write.c
src/sfm-write.h [new file with mode: 0644]
src/sfm.h [deleted file]
src/sfmP.h
src/split-file.c
src/str.c
src/str.h
src/sysfile-info.c
src/t-test.q
src/temporary.c
src/title.c
src/value-labels.c
src/var.h
src/vars-atr.c
src/vars-prs.c
src/vector.c
src/vfm.c
src/weight.c

index c74f702a931a88a7c2a2788e751421d996dd1467..aec2bf5653179bf9b7a1bc536def14297d7f1022 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"
 msgstr ""
 "Project-Id-Version: PSPP 0.3.1\n"
 "Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
-"POT-Creation-Date: 2004-11-09 08:18+0800\n"
+"POT-Creation-Date: 2004-11-15 00:28-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"
 "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,91 +16,91 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n!=1);\n"
 
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n!=1);\n"
 
-#: src/aggregate.c:188 src/aggregate.c:223 src/data-list.c:1437
-#: src/data-list.c:1472 src/data-list.c:1485 src/data-list.c:1498
-#: src/data-list.c:1531
+#: src/aggregate.c:187 src/aggregate.c:220 src/data-list.c:1421
+#: src/data-list.c:1456 src/data-list.c:1469 src/data-list.c:1482
+#: src/data-list.c:1515
 #, c-format
 msgid "%s subcommand given multiple times."
 msgstr ""
 
 #, c-format
 msgid "%s subcommand given multiple times."
 msgstr ""
 
-#: src/aggregate.c:208
+#: src/aggregate.c:205
 msgid "while expecting COLUMNWISE"
 msgstr ""
 
 msgid "while expecting COLUMNWISE"
 msgstr ""
 
-#: src/aggregate.c:246
+#: src/aggregate.c:243
 msgid "BREAK subcommand not specified."
 msgstr ""
 
 msgid "BREAK subcommand not specified."
 msgstr ""
 
-#: src/aggregate.c:416
+#: src/aggregate.c:397
 msgid "expecting aggregation function"
 msgstr ""
 
 msgid "expecting aggregation function"
 msgstr ""
 
-#: src/aggregate.c:432
+#: src/aggregate.c:413
 #, c-format
 msgid "Unknown aggregation function %s."
 msgstr ""
 
 #, c-format
 msgid "Unknown aggregation function %s."
 msgstr ""
 
-#: src/aggregate.c:447
+#: src/aggregate.c:428
 msgid "expecting `('"
 msgstr ""
 
 msgid "expecting `('"
 msgstr ""
 
-#: src/aggregate.c:482
+#: src/aggregate.c:463
 #, c-format
 msgid "Missing argument %d to %s."
 msgstr ""
 
 #, c-format
 msgid "Missing argument %d to %s."
 msgstr ""
 
-#: src/aggregate.c:490
+#: src/aggregate.c:471
 #, c-format
 msgid "Arguments to %s must be of same type as source variables."
 msgstr ""
 
 #, c-format
 msgid "Arguments to %s must be of same type as source variables."
 msgstr ""
 
-#: src/aggregate.c:500 src/expr-prs.c:626
+#: src/aggregate.c:481 src/expr-prs.c:627
 msgid "expecting `)'"
 msgstr ""
 
 msgid "expecting `)'"
 msgstr ""
 
-#: src/aggregate.c:512
+#: src/aggregate.c:493
 #, c-format
 msgid ""
 "Number of source variables (%d) does not match number of target variables (%"
 "d)."
 msgstr ""
 
 #, c-format
 msgid ""
 "Number of source variables (%d) does not match number of target variables (%"
 "d)."
 msgstr ""
 
-#: src/aggregate.c:581
+#: src/aggregate.c:562
 #, c-format
 msgid ""
 "Variable name %s is not unique within the aggregate file dictionary, which "
 "contains the aggregate variables and the break variables."
 msgstr ""
 
 #, c-format
 msgid ""
 "Variable name %s is not unique within the aggregate file dictionary, which "
 "contains the aggregate variables and the break variables."
 msgstr ""
 
-#: src/apply-dict.c:65
+#: src/apply-dict.c:68
 #, c-format
 msgid "Variable %s is %s in target file, but %s in source file."
 msgstr ""
 
 #, c-format
 msgid "Variable %s is %s in target file, but %s in source file."
 msgstr ""
 
-#: src/apply-dict.c:68 src/apply-dict.c:69 src/expr-prs.c:1288
-#: src/expr-prs.c:1304 src/formats.c:96 src/pfm-read.c:636 src/print.c:689
-#: src/sfm-read.c:927 src/sfm-read.c:1057 src/sfm-read.c:1058
+#: src/apply-dict.c:71 src/apply-dict.c:72 src/expr-prs.c:1289
+#: src/expr-prs.c:1305 src/formats.c:96 src/pfm-read.c:604 src/print.c:695
+#: src/sfm-read.c:888 src/sfm-read.c:1017 src/sfm-read.c:1018
 msgid "string"
 msgstr ""
 
 msgid "string"
 msgstr ""
 
-#: src/apply-dict.c:68 src/apply-dict.c:69 src/expr-prs.c:1285
-#: src/expr-prs.c:1302 src/formats.c:96 src/pfm-read.c:636 src/print.c:689
-#: src/sfm-read.c:927 src/sfm-read.c:1057 src/sfm-read.c:1058
+#: src/apply-dict.c:71 src/apply-dict.c:72 src/expr-prs.c:1286
+#: src/expr-prs.c:1303 src/formats.c:96 src/pfm-read.c:604 src/print.c:695
+#: src/sfm-read.c:888 src/sfm-read.c:1017 src/sfm-read.c:1018
 msgid "numeric"
 msgstr ""
 
 msgid "numeric"
 msgstr ""
 
-#: src/apply-dict.c:81
+#: src/apply-dict.c:84
 #, c-format
 msgid "Cannot add value labels from source file to long string variable %s."
 msgstr ""
 
 #, c-format
 msgid "Cannot add value labels from source file to long string variable %s."
 msgstr ""
 
-#: src/apply-dict.c:127
+#: src/apply-dict.c:130
 #, c-format
 msgid ""
 "Cannot apply missing values from source file to long string variable %s."
 msgstr ""
 
 #, c-format
 msgid ""
 "Cannot apply missing values from source file to long string variable %s."
 msgstr ""
 
-#: src/apply-dict.c:160
+#: src/apply-dict.c:163
 msgid "No matching variables found between the source and target files."
 msgstr ""
 
 msgid "No matching variables found between the source and target files."
 msgstr ""
 
@@ -219,65 +219,65 @@ msgstr ""
 msgid "%s - Page %d"
 msgstr ""
 
 msgid "%s - Page %d"
 msgstr ""
 
-#: src/autorecode.c:121
+#: src/autorecode.c:122
 #, c-format
 msgid "Source variable count (%d) does not match target variable count (%d)."
 msgstr ""
 
 #, c-format
 msgid "Source variable count (%d) does not match target variable count (%d)."
 msgstr ""
 
-#: src/autorecode.c:138 src/command.c:738 src/compute.c:293
-#: src/data-list.c:406 src/data-list.c:903 src/data-list.c:1764
-#: src/do-if.c:253 src/get.c:351 src/lexer.c:412 src/loop.c:240
-#: src/matrix-data.c:504 src/print.c:329 src/print.c:1039 src/recode.c:404
-#: src/sel-if.c:53 src/sel-if.c:130 src/vector.c:192 src/file-handle.q:141
+#: src/autorecode.c:139 src/command.c:739 src/compute.c:294
+#: src/data-list.c:407 src/data-list.c:897 src/data-list.c:1748
+#: src/do-if.c:253 src/get.c:405 src/lexer.c:412 src/loop.c:241
+#: src/matrix-data.c:527 src/print.c:335 src/print.c:1045 src/recode.c:405
+#: src/sel-if.c:54 src/sel-if.c:131 src/vector.c:193 src/file-handle.q:138
 msgid "expecting end of command"
 msgstr ""
 
 msgid "expecting end of command"
 msgstr ""
 
-#: src/autorecode.c:148
+#: src/autorecode.c:149
 #, c-format
 msgid "Target variable %s duplicates existing variable %s."
 msgstr ""
 
 #, c-format
 msgid "Target variable %s duplicates existing variable %s."
 msgstr ""
 
-#: src/autorecode.c:155
+#: src/autorecode.c:156
 #, c-format
 msgid "Duplicate variable name %s among target variables."
 msgstr ""
 
 #, c-format
 msgid "Duplicate variable name %s among target variables."
 msgstr ""
 
-#: src/casefile.c:184
+#: src/casefile.c:182
 #, c-format
 msgid "%s: Removing temporary file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Removing temporary file: %s."
 msgstr ""
 
-#: src/casefile.c:329
+#: src/casefile.c:328
 #, c-format
 msgid "Error writing temporary file: %s."
 msgstr ""
 
 #, c-format
 msgid "Error writing temporary file: %s."
 msgstr ""
 
-#: src/casefile.c:356
+#: src/casefile.c:355
 #, c-format
 msgid "%s: Creating temporary file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Creating temporary file: %s."
 msgstr ""
 
-#: src/casefile.c:500
+#: src/casefile.c:498
 #, c-format
 msgid "%s: Opening temporary file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Opening temporary file: %s."
 msgstr ""
 
-#: src/casefile.c:526
+#: src/casefile.c:524
 #, c-format
 msgid "%s: Seeking temporary file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Seeking temporary file: %s."
 msgstr ""
 
-#: src/casefile.c:541
+#: src/casefile.c:540
 #, c-format
 msgid "%s: Reading temporary file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Reading temporary file: %s."
 msgstr ""
 
-#: src/casefile.c:544
+#: src/casefile.c:543
 #, c-format
 msgid "%s: Temporary file ended unexpectedly."
 msgstr ""
 
 #, c-format
 msgid "%s: Temporary file ended unexpectedly."
 msgstr ""
 
-#: src/cmdline.c:141 src/cmdline.c:160 src/cmdline.c:172 src/command.c:160
+#: src/cmdline.c:141 src/cmdline.c:160 src/cmdline.c:172 src/command.c:161
 #: src/set.q:414 src/set.q:416 src/set.q:956
 #, c-format
 msgid "%s is not yet implemented."
 #: src/set.q:414 src/set.q:416 src/set.q:956
 #, c-format
 msgid "%s is not yet implemented."
@@ -350,25 +350,25 @@ msgid ""
 "Report bugs to <%s>.\n"
 msgstr ""
 
 "Report bugs to <%s>.\n"
 msgstr ""
 
-#: src/command.c:98
+#: src/command.c:99
 #, c-format
 msgid "%s not allowed inside FILE TYPE/END FILE TYPE."
 msgstr ""
 
 #, c-format
 msgid "%s not allowed inside FILE TYPE/END FILE TYPE."
 msgstr ""
 
-#: src/command.c:102
+#: src/command.c:103
 #, c-format
 msgid "%s not allowed inside FILE TYPE GROUPED/END FILE TYPE."
 msgstr ""
 
 #, c-format
 msgid "%s not allowed inside FILE TYPE GROUPED/END FILE TYPE."
 msgstr ""
 
-#: src/command.c:105
+#: src/command.c:106
 msgid "RECORD TYPE must be the first command inside a FILE TYPE structure."
 msgstr ""
 
 msgid "RECORD TYPE must be the first command inside a FILE TYPE structure."
 msgstr ""
 
-#: src/command.c:150
+#: src/command.c:151
 msgid "expecting command name"
 msgstr ""
 
 msgid "expecting command name"
 msgstr ""
 
-#: src/command.c:179
+#: src/command.c:180
 #, c-format
 msgid ""
 "%s is not allowed (1) before a command to specify the input program, such as "
 #, c-format
 msgid ""
 "%s is not allowed (1) before a command to specify the input program, such as "
@@ -376,83 +376,83 @@ msgid ""
 "PROGRAM and END INPUT PROGRAM."
 msgstr ""
 
 "PROGRAM and END INPUT PROGRAM."
 msgstr ""
 
-#: src/command.c:183
+#: src/command.c:184
 #, c-format
 msgid "%s is not allowed within an input program."
 msgstr ""
 
 #, c-format
 msgid "%s is not allowed within an input program."
 msgstr ""
 
-#: src/command.c:184 src/command.c:185
+#: src/command.c:185 src/command.c:186
 #, c-format
 msgid "%s is only allowed within an input program."
 msgstr ""
 
 #, c-format
 msgid "%s is only allowed within an input program."
 msgstr ""
 
-#: src/command.c:464
+#: src/command.c:465
 #, c-format
 msgid "Unknown command %s."
 msgstr ""
 
 #, c-format
 msgid "Unknown command %s."
 msgstr ""
 
-#: src/command.c:564
+#: src/command.c:565
 msgid ""
 "This command is not accepted in a syntax file.  Instead, use FINISH to "
 "terminate a syntax file."
 msgstr ""
 
 msgid ""
 "This command is not accepted in a syntax file.  Instead, use FINISH to "
 "terminate a syntax file."
 msgstr ""
 
-#: src/command.c:582
+#: src/command.c:583
 msgid ""
 "This command is not executed in interactive mode.  Instead, PSPP drops down "
 "to the command prompt.  Use EXIT if you really want to quit."
 msgstr ""
 
 msgid ""
 "This command is not executed in interactive mode.  Instead, PSPP drops down "
 "to the command prompt.  Use EXIT if you really want to quit."
 msgstr ""
 
-#: src/command.c:625 src/command.c:756
+#: src/command.c:626 src/command.c:757
 msgid "This command not allowed when the SAFER option is set."
 msgstr ""
 
 msgid "This command not allowed when the SAFER option is set."
 msgstr ""
 
-#: src/command.c:637
+#: src/command.c:638
 #, c-format
 msgid "Error removing `%s': %s."
 msgstr ""
 
 #, c-format
 msgid "Error removing `%s': %s."
 msgstr ""
 
-#: src/command.c:687
+#: src/command.c:688
 #, c-format
 msgid "Couldn't fork: %s."
 msgstr ""
 
 #, c-format
 msgid "Couldn't fork: %s."
 msgstr ""
 
-#: src/command.c:729
+#: src/command.c:730
 #, c-format
 msgid "Error executing command: %s."
 msgstr ""
 
 #, c-format
 msgid "Error executing command: %s."
 msgstr ""
 
-#: src/command.c:777
+#: src/command.c:778
 msgid "No operating system support for this command."
 msgstr ""
 
 msgid "No operating system support for this command."
 msgstr ""
 
-#: src/command.c:800
+#: src/command.c:801
 msgid "This command is not valid in a syntax file."
 msgstr ""
 
 msgid "This command is not valid in a syntax file."
 msgstr ""
 
-#: src/compute.c:145 src/compute.c:209
+#: src/compute.c:146 src/compute.c:210
 #, c-format
 msgid ""
 "When executing COMPUTE: SYSMIS is not a valid value as an index into vector %"
 "s."
 msgstr ""
 
 #, c-format
 msgid ""
 "When executing COMPUTE: SYSMIS is not a valid value as an index into vector %"
 "s."
 msgstr ""
 
-#: src/compute.c:148 src/compute.c:213
+#: src/compute.c:149 src/compute.c:214
 #, c-format
 msgid ""
 "When executing COMPUTE: %g is not a valid value as an index into vector %s."
 msgstr ""
 
 #, c-format
 msgid ""
 "When executing COMPUTE: %g is not a valid value as an index into vector %s."
 msgstr ""
 
-#: src/compute.c:355
+#: src/compute.c:356
 #, c-format
 msgid "There is no vector named %s."
 msgstr ""
 
 #, c-format
 msgid "There is no vector named %s."
 msgstr ""
 
-#: src/count.c:155
+#: src/count.c:156
 msgid "Destination cannot be a string variable."
 msgstr ""
 
 msgid "Destination cannot be a string variable."
 msgstr ""
 
-#: src/count.c:262
+#: src/count.c:263
 #, c-format
 msgid ""
 "%g THRU %g is not a valid range.  The number following THRU must be at least "
 #, c-format
 msgid ""
 "%g THRU %g is not a valid range.  The number following THRU must be at least "
@@ -654,259 +654,259 @@ msgstr ""
 msgid "Only one of FIXED, FREE, or LIST may be specified."
 msgstr ""
 
 msgid "Only one of FIXED, FREE, or LIST may be specified."
 msgstr ""
 
-#: src/data-list.c:353 src/print.c:290
+#: src/data-list.c:354 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 ""
 
 #, 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:382 src/data-list.c:1753
+#: src/data-list.c:383 src/data-list.c:1737
 msgid ""
 "SPSS-like or FORTRAN-like format specification expected after variable names."
 msgstr ""
 
 msgid ""
 "SPSS-like or FORTRAN-like format specification expected after variable names."
 msgstr ""
 
-#: src/data-list.c:393
+#: src/data-list.c:394
 msgid "At least one variable must be specified."
 msgstr ""
 
 msgid "At least one variable must be specified."
 msgstr ""
 
-#: src/data-list.c:398 src/print.c:322
+#: src/data-list.c:399 src/print.c:328
 msgid ""
 "Variables are specified on records that should not exist according to "
 "RECORDS subcommand."
 msgstr ""
 
 msgid ""
 "Variables are specified on records that should not exist according to "
 "RECORDS subcommand."
 msgstr ""
 
-#: src/data-list.c:436 src/data-list.c:450 src/print.c:499 src/print.c:512
+#: src/data-list.c:437 src/data-list.c:451 src/print.c:505 src/print.c:518
 msgid "Column positions for fields must be positive."
 msgstr ""
 
 msgid "Column positions for fields must be positive."
 msgstr ""
 
-#: src/data-list.c:455
+#: src/data-list.c:456
 msgid "The ending column for a field must be greater than the starting column."
 msgstr ""
 
 msgid "The ending column for a field must be greater than the starting column."
 msgstr ""
 
-#: src/data-list.c:469 src/print.c:589
+#: src/data-list.c:470 src/print.c:595
 #, c-format
 msgid "The %d columns %d-%d can't be evenly divided into %d fields."
 msgstr ""
 
 #, c-format
 msgid "The %d columns %d-%d can't be evenly divided into %d fields."
 msgstr ""
 
-#: src/data-list.c:489 src/print.c:540
+#: src/data-list.c:490 src/print.c:546
 msgid "A format specifier on this line has extra characters on the end."
 msgstr ""
 
 msgid "A format specifier on this line has extra characters on the end."
 msgstr ""
 
-#: src/data-list.c:504 src/print.c:556
+#: src/data-list.c:505 src/print.c:562
 msgid "The value for number of decimal places must be at least 1."
 msgstr ""
 
 msgid "The value for number of decimal places must be at least 1."
 msgstr ""
 
-#: src/data-list.c:518 src/print.c:569
+#: src/data-list.c:519 src/print.c:575
 #, c-format
 msgid "Input format %s doesn't accept decimal places."
 msgstr ""
 
 #, c-format
 msgid "Input format %s doesn't accept decimal places."
 msgstr ""
 
-#: src/data-list.c:565 src/data-list.c:661 src/data-list.c:882
+#: src/data-list.c:566 src/data-list.c:662 src/data-list.c:876
 #, c-format
 msgid "%s is a duplicate variable name."
 msgstr ""
 
 #, c-format
 msgid "%s is a duplicate variable name."
 msgstr ""
 
-#: src/data-list.c:570
+#: src/data-list.c:571
 #, c-format
 msgid "There is already a variable %s of a different type."
 msgstr ""
 
 #, c-format
 msgid "There is already a variable %s of a different type."
 msgstr ""
 
-#: src/data-list.c:577
+#: src/data-list.c:578
 #, c-format
 msgid "There is already a string variable %s of a different width."
 msgstr ""
 
 #, c-format
 msgid "There is already a string variable %s of a different width."
 msgstr ""
 
-#: src/data-list.c:652
+#: src/data-list.c:653
 msgid ""
 "The number of format specifications exceeds the given number of variable "
 "names."
 msgstr ""
 
 msgid ""
 "The number of format specifications exceeds the given number of variable "
 "names."
 msgstr ""
 
-#: src/data-list.c:765 src/print.c:762
+#: src/data-list.c:766 src/print.c:768
 msgid ""
 "There aren't enough format specifications to match the number of variable "
 "names given."
 msgstr ""
 
 msgid ""
 "There aren't enough format specifications to match the number of variable "
 "names given."
 msgstr ""
 
-#: src/data-list.c:794 src/data-list.c:924 src/descript.c:879 src/print.c:793
-#: src/sysfile-info.c:132 src/sysfile-info.c:365 src/vfm.c:874
+#: src/data-list.c:793 src/data-list.c:919 src/descript.c:880 src/print.c:799
+#: src/sysfile-info.c:134 src/sysfile-info.c:367 src/vfm.c:875
 msgid "Variable"
 msgstr ""
 
 msgid "Variable"
 msgstr ""
 
-#: src/data-list.c:795 src/print.c:794
+#: src/data-list.c:794 src/print.c:800
 msgid "Record"
 msgstr ""
 
 msgid "Record"
 msgstr ""
 
-#: src/data-list.c:796 src/print.c:795
+#: src/data-list.c:795 src/print.c:801
 msgid "Columns"
 msgstr ""
 
 msgid "Columns"
 msgstr ""
 
-#: src/data-list.c:797 src/data-list.c:925 src/print.c:796
+#: src/data-list.c:796 src/data-list.c:920 src/print.c:802
 msgid "Format"
 msgstr ""
 
 msgid "Format"
 msgstr ""
 
-#: src/data-list.c:817
+#: src/data-list.c:812
 #, c-format
 msgid "Reading %d record from file %s."
 msgid_plural "Reading %d records from file %s."
 msgstr[0] ""
 msgstr[1] ""
 
 #, c-format
 msgid "Reading %d record from file %s."
 msgid_plural "Reading %d records from file %s."
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/data-list.c:819
+#: src/data-list.c:816
 #, c-format
 msgid "Reading %d record from the command file."
 msgid_plural "Reading %d records from the command file."
 msgstr[0] ""
 msgstr[1] ""
 
 #, 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:948
+#: src/data-list.c:936
 #, c-format
 msgid "Reading free-form data from file %s."
 msgstr ""
 
 #, c-format
 msgid "Reading free-form data from file %s."
 msgstr ""
 
-#: src/data-list.c:949
+#: src/data-list.c:939
 msgid "Reading free-form data from the command file."
 msgstr ""
 
 msgid "Reading free-form data from the command file."
 msgstr ""
 
-#: src/data-list.c:1002
+#: src/data-list.c:990
 #, c-format
 msgid "Quoted string missing terminating `%c'."
 msgstr ""
 
 #, c-format
 msgid "Quoted string missing terminating `%c'."
 msgstr ""
 
-#: src/data-list.c:1111
+#: src/data-list.c:1099
 #, c-format
 msgid "Partial case of %d of %d records discarded."
 msgstr ""
 
 #, c-format
 msgid "Partial case of %d of %d records discarded."
 msgstr ""
 
-#: src/data-list.c:1165
+#: src/data-list.c:1153
 #, c-format
 msgid "Partial case discarded.  The first variable missing was %s."
 msgstr ""
 
 #, c-format
 msgid "Partial case discarded.  The first variable missing was %s."
 msgstr ""
 
-#: src/data-list.c:1209
+#: src/data-list.c:1197
 #, 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 ""
 
 #, 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:1287
+#: src/data-list.c:1275
 msgid "Attempt to read past end of file."
 msgstr ""
 
 msgid "Attempt to read past end of file."
 msgstr ""
 
-#: src/data-list.c:1427
+#: src/data-list.c:1411
 msgid ""
 "REPEATING DATA must use the same file as its corresponding DATA LIST or FILE "
 "TYPE."
 msgstr ""
 
 msgid ""
 "REPEATING DATA must use the same file as its corresponding DATA LIST or FILE "
 "TYPE."
 msgstr ""
 
-#: src/data-list.c:1461
+#: src/data-list.c:1445
 #, c-format
 msgid "STARTS beginning column (%d) exceeds STARTS ending column (%d)."
 msgstr ""
 
 #, c-format
 msgid "STARTS beginning column (%d) exceeds STARTS ending column (%d)."
 msgstr ""
 
-#: src/data-list.c:1517
+#: src/data-list.c:1501
 #, c-format
 msgid "CONTINUED beginning column (%d) exceeds CONTINUED ending column (%d)."
 msgstr ""
 
 #, c-format
 msgid "CONTINUED beginning column (%d) exceeds CONTINUED ending column (%d)."
 msgstr ""
 
-#: src/data-list.c:1540
+#: src/data-list.c:1524
 #, c-format
 msgid "ID beginning column (%ld) must be positive."
 msgstr ""
 
 #, c-format
 msgid "ID beginning column (%ld) must be positive."
 msgstr ""
 
-#: src/data-list.c:1555
+#: src/data-list.c:1539
 #, c-format
 msgid "ID ending column (%ld) must be positive."
 msgstr ""
 
 #, c-format
 msgid "ID ending column (%ld) must be positive."
 msgstr ""
 
-#: src/data-list.c:1561
+#: src/data-list.c:1545
 #, c-format
 msgid "ID ending column (%ld) cannot be less than ID beginning column (%d)."
 msgstr ""
 
 #, c-format
 msgid "ID ending column (%ld) cannot be less than ID beginning column (%d)."
 msgstr ""
 
-#: src/data-list.c:1601
+#: src/data-list.c:1585
 msgid "Missing required specification STARTS."
 msgstr ""
 
 msgid "Missing required specification STARTS."
 msgstr ""
 
-#: src/data-list.c:1603
+#: src/data-list.c:1587
 msgid "Missing required specification OCCURS."
 msgstr ""
 
 msgid "Missing required specification OCCURS."
 msgstr ""
 
-#: src/data-list.c:1610
+#: src/data-list.c:1594
 msgid "ID specified without CONTINUED."
 msgstr ""
 
 msgid "ID specified without CONTINUED."
 msgstr ""
 
-#: src/data-list.c:1702
+#: src/data-list.c:1686
 msgid "String variable not allowed here."
 msgstr ""
 
 msgid "String variable not allowed here."
 msgstr ""
 
-#: src/data-list.c:1712
+#: src/data-list.c:1696
 #, c-format
 msgid "%s (%d) must be at least 1."
 msgstr ""
 
 #, c-format
 msgid "%s (%d) must be at least 1."
 msgstr ""
 
-#: src/data-list.c:1718
+#: src/data-list.c:1702
 #, c-format
 msgid "Variable or integer expected for %s."
 msgstr ""
 
 #, c-format
 msgid "Variable or integer expected for %s."
 msgstr ""
 
-#: src/data-list.c:1856
+#: src/data-list.c:1840
 #, c-format
 msgid "Encountered mismatched record ID \"%s\" expecting \"%s\"."
 msgstr ""
 
 #, c-format
 msgid "Encountered mismatched record ID \"%s\" expecting \"%s\"."
 msgstr ""
 
-#: src/data-list.c:1888
+#: src/data-list.c:1872
 #, c-format
 msgid ""
 "Variable %s starting in column %d extends beyond physical record length of %"
 "d."
 msgstr ""
 
 #, c-format
 msgid ""
 "Variable %s starting in column %d extends beyond physical record length of %"
 "d."
 msgstr ""
 
-#: src/data-list.c:1956
+#: src/data-list.c:1940
 #, c-format
 msgid "Invalid value %d for OCCURS."
 msgstr ""
 
 #, c-format
 msgid "Invalid value %d for OCCURS."
 msgstr ""
 
-#: src/data-list.c:1962
+#: src/data-list.c:1946
 #, c-format
 msgid "Beginning column for STARTS (%d) must be at least 1."
 msgstr ""
 
 #, c-format
 msgid "Beginning column for STARTS (%d) must be at least 1."
 msgstr ""
 
-#: src/data-list.c:1970
+#: src/data-list.c:1954
 #, c-format
 msgid "Ending column for STARTS (%d) is less than beginning column (%d)."
 msgstr ""
 
 #, c-format
 msgid "Ending column for STARTS (%d) is less than beginning column (%d)."
 msgstr ""
 
-#: src/data-list.c:1978
+#: src/data-list.c:1962
 #, c-format
 msgid "Invalid value %d for LENGTH."
 msgstr ""
 
 #, c-format
 msgid "Invalid value %d for LENGTH."
 msgstr ""
 
-#: src/data-list.c:1985
+#: src/data-list.c:1969
 #, c-format
 msgid "Beginning column for CONTINUED (%d) must be at least 1."
 msgstr ""
 
 #, c-format
 msgid "Beginning column for CONTINUED (%d) must be at least 1."
 msgstr ""
 
-#: src/data-list.c:1993
+#: src/data-list.c:1977
 #, c-format
 msgid "Ending column for CONTINUED (%d) is less than beginning column (%d)."
 msgstr ""
 
 #, c-format
 msgid "Ending column for CONTINUED (%d) is less than beginning column (%d)."
 msgstr ""
 
-#: src/data-list.c:2025
+#: src/data-list.c:2009
 #, 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 ""
 
 #, 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:2043
+#: src/data-list.c:2027
 #, c-format
 msgid "Unexpected end of file with %d repetitions remaining out of %d."
 msgstr ""
 
 #, c-format
 msgid "Unexpected end of file with %d repetitions remaining out of %d."
 msgstr ""
 
-#: src/data-out.c:235 src/sfm-read.c:466 src/sysfile-info.c:113
+#: src/data-out.c:235 src/sfm-read.c:432 src/sysfile-info.c:115
 msgid "Unknown"
 msgstr ""
 
 msgid "Unknown"
 msgstr ""
 
@@ -957,99 +957,99 @@ msgstr ""
 msgid "Only USE ALL is currently implemented."
 msgstr ""
 
 msgid "Only USE ALL is currently implemented."
 msgstr ""
 
-#: src/descript.c:98 src/examine.q:584 src/frequencies.q:100 src/oneway.q:394
-#: src/t-test.q:681 src/t-test.q:704 src/t-test.q:827 src/t-test.q:1165
+#: src/descript.c:99 src/examine.q:631 src/frequencies.q:110 src/oneway.q:396
+#: src/t-test.q:683 src/t-test.q:706 src/t-test.q:829 src/t-test.q:1166
 msgid "Mean"
 msgstr ""
 
 msgid "Mean"
 msgstr ""
 
-#: src/descript.c:99
+#: src/descript.c:100
 msgid "S E Mean"
 msgstr ""
 
 msgid "S E Mean"
 msgstr ""
 
-#: src/descript.c:100 src/frequencies.q:104
+#: src/descript.c:101 src/frequencies.q:114
 msgid "Std Dev"
 msgstr ""
 
 msgid "Std Dev"
 msgstr ""
 
-#: src/descript.c:101 src/examine.q:641 src/frequencies.q:105
+#: src/descript.c:102 src/examine.q:694 src/frequencies.q:115
 msgid "Variance"
 msgstr ""
 
 msgid "Variance"
 msgstr ""
 
-#: src/descript.c:102 src/examine.q:711 src/frequencies.q:106
+#: src/descript.c:103 src/examine.q:764 src/frequencies.q:116
 msgid "Kurtosis"
 msgstr ""
 
 msgid "Kurtosis"
 msgstr ""
 
-#: src/descript.c:103
+#: src/descript.c:104
 msgid "S E Kurt"
 msgstr ""
 
 msgid "S E Kurt"
 msgstr ""
 
-#: src/descript.c:104 src/examine.q:706 src/frequencies.q:108
+#: src/descript.c:105 src/examine.q:759 src/frequencies.q:118
 msgid "Skewness"
 msgstr ""
 
 msgid "Skewness"
 msgstr ""
 
-#: src/descript.c:105
+#: src/descript.c:106
 msgid "S E Skew"
 msgstr ""
 
 msgid "S E Skew"
 msgstr ""
 
-#: src/descript.c:106 src/examine.q:689 src/frequencies.q:110
+#: src/descript.c:107 src/examine.q:742 src/frequencies.q:120
 msgid "Range"
 msgstr ""
 
 msgid "Range"
 msgstr ""
 
-#: src/descript.c:107 src/examine.q:666 src/frequencies.q:111 src/oneway.q:406
+#: src/descript.c:108 src/examine.q:719 src/frequencies.q:121 src/oneway.q:408
 msgid "Minimum"
 msgstr ""
 
 msgid "Minimum"
 msgstr ""
 
-#: src/descript.c:108 src/examine.q:677 src/frequencies.q:112 src/oneway.q:407
+#: src/descript.c:109 src/examine.q:730 src/frequencies.q:122 src/oneway.q:409
 msgid "Maximum"
 msgstr ""
 
 msgid "Maximum"
 msgstr ""
 
-#: src/descript.c:109 src/frequencies.q:113
+#: src/descript.c:110 src/frequencies.q:123
 msgid "Sum"
 msgstr ""
 
 msgid "Sum"
 msgstr ""
 
-#: src/descript.c:332
+#: src/descript.c:333
 #, c-format
 msgid "Z-score variable name %s would be a duplicate variable name."
 msgstr ""
 
 #, c-format
 msgid "Z-score variable name %s would be a duplicate variable name."
 msgstr ""
 
-#: src/descript.c:350 src/list.q:140
+#: src/descript.c:351 src/list.q:142
 msgid "No variables specified."
 msgstr ""
 
 msgid "No variables specified."
 msgstr ""
 
-#: src/descript.c:434
+#: src/descript.c:435
 msgid "expecting statistic name: reverting to default"
 msgstr ""
 
 msgid "expecting statistic name: reverting to default"
 msgstr ""
 
-#: src/descript.c:507
+#: src/descript.c:508
 msgid ""
 "Ran out of generic names for Z-score variables.  There are only 126 generic "
 "names: ZSC001-ZSC0999, STDZ01-STDZ09, ZZZZ01-ZZZZ09, ZQZQ01-ZQZQ09."
 msgstr ""
 
 msgid ""
 "Ran out of generic names for Z-score variables.  There are only 126 generic "
 "names: ZSC001-ZSC0999, STDZ01-STDZ09, ZZZZ01-ZZZZ09, ZQZQ01-ZQZQ09."
 msgstr ""
 
-#: src/descript.c:538
+#: src/descript.c:539
 msgid "Mapping of variables to corresponding Z-scores."
 msgstr ""
 
 msgid "Mapping of variables to corresponding Z-scores."
 msgstr ""
 
-#: src/descript.c:543
+#: src/descript.c:544
 msgid "Source"
 msgstr ""
 
 msgid "Source"
 msgstr ""
 
-#: src/descript.c:544
+#: src/descript.c:545
 msgid "Target"
 msgstr ""
 
 msgid "Target"
 msgstr ""
 
-#: src/descript.c:663 src/descript.c:669
+#: src/descript.c:664 src/descript.c:670
 msgid "Z-score of "
 msgstr ""
 
 msgid "Z-score of "
 msgstr ""
 
-#: src/descript.c:882
+#: src/descript.c:883
 msgid "Valid N"
 msgstr ""
 
 msgid "Valid N"
 msgstr ""
 
-#: src/descript.c:883
+#: src/descript.c:884
 msgid "Missing N"
 msgstr ""
 
 msgid "Missing N"
 msgstr ""
 
-#: src/descript.c:909
+#: src/descript.c:910
 #, c-format
 msgid "Valid cases = %g; cases with missing value(s) = %g."
 msgstr ""
 #, c-format
 msgid "Valid cases = %g; cases with missing value(s) = %g."
 msgstr ""
@@ -1074,99 +1074,58 @@ msgstr ""
 msgid "Cannot open first page on DEVIND device %s."
 msgstr ""
 
 msgid "Cannot open first page on DEVIND device %s."
 msgstr ""
 
-#: src/dfm.c:88 src/dfm.c:565
-#, c-format
-msgid "%s: Closing data-file handle %s."
-msgstr ""
-
-#: src/dfm.c:115
-#, c-format
-msgid "Cannot read from file %s already opened for %s."
-msgstr ""
-
-#: src/dfm.c:129
-#, c-format
-msgid "%s: Opening data-file handle %s for reading."
-msgstr ""
-
-#: src/dfm.c:144
+#: src/dfm-read.c:153
 #, c-format
 msgid "Could not open \"%s\" for reading as a data file: %s."
 msgstr ""
 
 #, c-format
 msgid "Could not open \"%s\" for reading as a data file: %s."
 msgstr ""
 
-#: src/dfm.c:186 src/dfm.c:204
+#: src/dfm-read.c:186 src/dfm-read.c:204
 msgid "BEGIN DATA expected."
 msgstr ""
 
 msgid "BEGIN DATA expected."
 msgstr ""
 
-#: src/dfm.c:213
+#: src/dfm-read.c:213
 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 ""
 
 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.c:241 src/dfm.c:261
+#: src/dfm-read.c:246 src/dfm-read.c:266
 #, c-format
 msgid "Error reading file %s: %s."
 msgstr ""
 
 #, c-format
 msgid "Error reading file %s: %s."
 msgstr ""
 
-#: src/dfm.c:264
+#: src/dfm-read.c:269
 #, c-format
 msgid "%s: Partial record at end of file."
 msgstr ""
 
 #, c-format
 msgid "%s: Partial record at end of file."
 msgstr ""
 
-#: src/dfm.c:300
+#: src/dfm-read.c:312
 #, c-format
 msgid "Attempt to read beyond end-of-file on file %s."
 msgstr ""
 
 #, c-format
 msgid "Attempt to read beyond end-of-file on file %s."
 msgstr ""
 
-#: src/dfm.c:446
-msgid "reading as a data file"
-msgstr ""
-
-#: src/dfm.c:472
-#, c-format
-msgid "Cannot write to file %s already opened for %s."
-msgstr ""
-
-#: src/dfm.c:485
-#, c-format
-msgid "%s: Opening data-file handle %s for writing."
+#: src/dfm-read.c:315
+msgid "Attempt to read beyond END DATA."
 msgstr ""
 
 msgstr ""
 
-#: src/dfm.c:491
-msgid "Cannot open the inline file for writing."
+#: src/dfm-read.c:462
+msgid ""
+"This command is not valid here since the current input program does not "
+"access the inline file."
 msgstr ""
 
 msgstr ""
 
-#: src/dfm.c:505
+#: src/dfm-write.c:67
 #, c-format
 msgid "An error occurred while opening \"%s\" for writing as a data file: %s."
 msgstr ""
 
 #, c-format
 msgid "An error occurred while opening \"%s\" for writing as a data file: %s."
 msgstr ""
 
-#: src/dfm.c:547
+#: src/dfm-write.c:103
 #, c-format
 msgid "Error writing file %s: %s."
 msgstr ""
 
 #, c-format
 msgid "Error writing file %s: %s."
 msgstr ""
 
-#: src/dfm.c:581
-msgid "writing as a data file"
-msgstr ""
-
-#: src/dfm.c:598
-msgid ""
-"This command is not valid here since the current input program does not "
-"access the inline file."
-msgstr ""
-
-#: src/dfm.c:605
-msgid "inline file: Opening for reading."
-msgstr ""
-
-#: src/dfm.c:618
-msgid "Skipping remaining inline data."
-msgstr ""
-
-#: src/dictionary.c:583
+#: src/dictionary.c:603
 msgid ""
 "At least one case in the data file had a weight value that was user-missing, "
 "system-missing, zero, or negative.  These case(s) were ignored."
 msgid ""
 "At least one case in the data file had a weight value that was user-missing, "
 "system-missing, zero, or negative.  These case(s) were ignored."
@@ -1235,7 +1194,7 @@ msgstr ""
 msgid "fatal"
 msgstr ""
 
 msgid "fatal"
 msgstr ""
 
-#: src/error.c:259 src/error.c:266 src/error.c:269 src/expr-prs.c:1279
+#: src/error.c:259 src/error.c:266 src/error.c:269 src/expr-prs.c:1280
 msgid "error"
 msgstr ""
 
 msgid "error"
 msgstr ""
 
@@ -1251,54 +1210,54 @@ msgstr ""
 msgid "installation error"
 msgstr ""
 
 msgid "installation error"
 msgstr ""
 
-#: src/expr-evl.c:633
+#: src/expr-evl.c:634
 msgid "TIME.HMS cannot mix positive and negative in its arguments."
 msgstr ""
 
 msgid "TIME.HMS cannot mix positive and negative in its arguments."
 msgstr ""
 
-#: src/expr-evl.c:697
+#: src/expr-evl.c:698
 #, fuzzy
 msgid "Week argument to WKYR must be in range 0 to 53."
 msgstr "Text colour must be in range 0-15."
 
 #, fuzzy
 msgid "Week argument to WKYR must be in range 0 to 53."
 msgstr "Text colour must be in range 0-15."
 
-#: src/expr-evl.c:848 src/expr-evl.c:904
+#: src/expr-evl.c:849 src/expr-evl.c:905
 msgid "Argument 3 of RINDEX may not be system-missing."
 msgstr ""
 
 msgid "Argument 3 of RINDEX may not be system-missing."
 msgstr ""
 
-#: src/expr-evl.c:858 src/expr-evl.c:914
+#: src/expr-evl.c:859 src/expr-evl.c:915
 msgid ""
 "Argument 3 of RINDEX must be between 1 and the length of argument 2, and it "
 "must evenly divide the length of argument 2."
 msgstr ""
 
 msgid ""
 "Argument 3 of RINDEX must be between 1 and the length of argument 2, and it "
 "must evenly divide the length of argument 2."
 msgstr ""
 
-#: src/expr-evl.c:1100
+#: src/expr-evl.c:1101
 msgid ""
 "A number being treated as a Boolean in an expression was found to have a "
 "value other than 0 (false), 1 (true), or the system-missing value.  The "
 "result was forced to 0."
 msgstr ""
 
 msgid ""
 "A number being treated as a Boolean in an expression was found to have a "
 "value other than 0 (false), 1 (true), or the system-missing value.  The "
 "result was forced to 0."
 msgstr ""
 
-#: src/expr-evl.c:1141
+#: src/expr-evl.c:1142
 #, c-format
 msgid ""
 "SYSMIS is not a valid index value for vector %s.  The result will be set to "
 "SYSMIS."
 msgstr ""
 
 #, c-format
 msgid ""
 "SYSMIS is not a valid index value for vector %s.  The result will be set to "
 "SYSMIS."
 msgstr ""
 
-#: src/expr-evl.c:1145
+#: src/expr-evl.c:1146
 #, c-format
 msgid ""
 "%g is not a valid index value for vector %s.  The result will be set to "
 "SYSMIS."
 msgstr ""
 
 #, c-format
 msgid ""
 "%g is not a valid index value for vector %s.  The result will be set to "
 "SYSMIS."
 msgstr ""
 
-#: src/expr-evl.c:1164
+#: src/expr-evl.c:1165
 #, c-format
 msgid ""
 "SYSMIS is not a valid index value for vector %s.  The result will be set to "
 "the empty string."
 msgstr ""
 
 #, c-format
 msgid ""
 "SYSMIS is not a valid index value for vector %s.  The result will be set to "
 "the empty string."
 msgstr ""
 
-#: src/expr-evl.c:1169
+#: src/expr-evl.c:1170
 #, c-format
 msgid ""
 "%g is not a valid index value for vector %s.  The result will be set to the "
 #, c-format
 msgid ""
 "%g is not a valid index value for vector %s.  The result will be set to the "
@@ -1311,29 +1270,29 @@ msgid ""
 "Boolean value was found to have a constant value other than 0, 1, or SYSMIS."
 msgstr ""
 
 "Boolean value was found to have a constant value other than 0, 1, or SYSMIS."
 msgstr ""
 
-#: src/expr-prs.c:137
+#: src/expr-prs.c:138
 msgid ""
 "Type mismatch: expression has string type, but a numeric value is required "
 "here."
 msgstr ""
 
 msgid ""
 "Type mismatch: expression has string type, but a numeric value is required "
 "here."
 msgstr ""
 
-#: src/expr-prs.c:148
+#: src/expr-prs.c:149
 msgid ""
 "Type mismatch: expression has numeric type, but a string value is required "
 "here."
 msgstr ""
 
 msgid ""
 "Type mismatch: expression has numeric type, but a string value is required "
 "here."
 msgstr ""
 
-#: src/expr-prs.c:207
+#: src/expr-prs.c:208
 #, c-format
 msgid "Type mismatch: operands of %s operator must be strings."
 msgstr ""
 
 #, c-format
 msgid "Type mismatch: operands of %s operator must be strings."
 msgstr ""
 
-#: src/expr-prs.c:210
+#: src/expr-prs.c:211
 #, c-format
 msgid "Type mismatch: operands of %s operator must be numeric."
 msgstr ""
 
 #, c-format
 msgid "Type mismatch: operands of %s operator must be numeric."
 msgstr ""
 
-#: src/expr-prs.c:391
+#: src/expr-prs.c:392
 msgid ""
 "Chaining relational operators (e.g. \"a < b < c\") will not produce the "
 "mathematically expected result.  Use the AND logical operator to fix the "
 msgid ""
 "Chaining relational operators (e.g. \"a < b < c\") will not produce the "
 "mathematically expected result.  Use the AND logical operator to fix the "
@@ -1341,336 +1300,336 @@ msgid ""
 "parentheses will disable this warning (e.g. \"(a < b) < c\".)"
 msgstr ""
 
 "parentheses will disable this warning (e.g. \"(a < b) < c\".)"
 msgstr ""
 
-#: src/expr-prs.c:471
+#: src/expr-prs.c:472
 msgid ""
 "The exponentiation operator (\"**\") is left-associative, even though right-"
 "associative semantics are more useful.  That is, \"a**b**c\" equals \"(a**b)"
 "**c\", not as \"a**(b**c)\".  To disable this warning, insert parentheses."
 msgstr ""
 
 msgid ""
 "The exponentiation operator (\"**\") is left-associative, even though right-"
 "associative semantics are more useful.  That is, \"a**b**c\" equals \"(a**b)"
 "**c\", not as \"a**(b**c)\".  To disable this warning, insert parentheses."
 msgstr ""
 
-#: src/expr-prs.c:552
+#: src/expr-prs.c:553
 #, c-format
 msgid "Unknown system variable %s."
 msgstr ""
 
 #, c-format
 msgid "Unknown system variable %s."
 msgstr ""
 
-#: src/expr-prs.c:591
+#: src/expr-prs.c:592
 msgid "expecting variable name"
 msgstr ""
 
 msgid "expecting variable name"
 msgstr ""
 
-#: src/expr-prs.c:634
+#: src/expr-prs.c:635
 msgid "in expression"
 msgstr ""
 
 msgid "in expression"
 msgstr ""
 
-#: src/expr-prs.c:730
+#: src/expr-prs.c:731
 msgid "Argument 2 to LAG must be a small positive integer constant."
 msgstr ""
 
 msgid "Argument 2 to LAG must be a small positive integer constant."
 msgstr ""
 
-#: src/expr-prs.c:811 src/expr-prs.c:850
+#: src/expr-prs.c:812 src/expr-prs.c:851
 #, c-format
 msgid ""
 "Type mismatch in argument %d of %s, which was expected to be of %s type.  It "
 "was actually of %s type. "
 msgstr ""
 
 #, c-format
 msgid ""
 "Type mismatch in argument %d of %s, which was expected to be of %s type.  It "
 "was actually of %s type. "
 msgstr ""
 
-#: src/expr-prs.c:837
+#: src/expr-prs.c:838
 #, c-format
 msgid "%s cannot take Boolean operands."
 msgstr ""
 
 #, c-format
 msgid "%s cannot take Boolean operands."
 msgstr ""
 
-#: src/expr-prs.c:869
+#: src/expr-prs.c:870
 msgid "in function call"
 msgstr ""
 
 msgid "in function call"
 msgstr ""
 
-#: src/expr-prs.c:883
+#: src/expr-prs.c:884
 msgid "RANGE requires an odd number of arguments, but at least three."
 msgstr ""
 
 msgid "RANGE requires an odd number of arguments, but at least three."
 msgstr ""
 
-#: src/expr-prs.c:893
+#: src/expr-prs.c:894
 #, c-format
 msgid "%s requires at least two arguments."
 msgstr ""
 
 #, c-format
 msgid "%s requires at least two arguments."
 msgstr ""
 
-#: src/expr-prs.c:908
+#: src/expr-prs.c:909
 #, c-format
 msgid "%s.%d requires at least %d arguments."
 msgstr ""
 
 #, c-format
 msgid "%s.%d requires at least %d arguments."
 msgstr ""
 
-#: src/expr-prs.c:973
+#: src/expr-prs.c:974
 #, c-format
 msgid ""
 "Argument %d to CONCAT is type %s.  All arguments to CONCAT must be strings."
 msgstr ""
 
 #, c-format
 msgid ""
 "Argument %d to CONCAT is type %s.  All arguments to CONCAT must be strings."
 msgstr ""
 
-#: src/expr-prs.c:1070
+#: src/expr-prs.c:1071
 #, c-format
 msgid ""
 "Argument %d to %s was expected to be of %s type.  It was actually of type %s."
 msgstr ""
 
 #, c-format
 msgid ""
 "Argument %d to %s was expected to be of %s type.  It was actually of type %s."
 msgstr ""
 
-#: src/expr-prs.c:1087
+#: src/expr-prs.c:1088
 #, c-format
 msgid "%s is not a numeric format."
 msgstr ""
 
 #, c-format
 msgid "%s is not a numeric format."
 msgstr ""
 
-#: src/expr-prs.c:1125
+#: src/expr-prs.c:1126
 #, c-format
 msgid "Too few arguments to function %s."
 msgstr ""
 
 #, c-format
 msgid "Too few arguments to function %s."
 msgstr ""
 
-#: src/expr-prs.c:1158
+#: src/expr-prs.c:1159
 #, c-format
 msgid ""
 "Type mismatch in argument %d of %s.  A string expression was supplied where "
 "only a numeric expression is allowed."
 msgstr ""
 
 #, c-format
 msgid ""
 "Type mismatch in argument %d of %s.  A string expression was supplied where "
 "only a numeric expression is allowed."
 msgstr ""
 
-#: src/expr-prs.c:1168
+#: src/expr-prs.c:1169
 #, c-format
 msgid "Missing comma following argument %d of %s."
 msgstr ""
 
 #, c-format
 msgid "Missing comma following argument %d of %s."
 msgstr ""
 
-#: src/expr-prs.c:1206
+#: src/expr-prs.c:1207
 msgid "The index value after a vector name must be numeric."
 msgstr ""
 
 msgid "The index value after a vector name must be numeric."
 msgstr ""
 
-#: src/expr-prs.c:1213
+#: src/expr-prs.c:1214
 msgid "`)' expected after a vector index value."
 msgstr ""
 
 msgid "`)' expected after a vector index value."
 msgstr ""
 
-#: src/expr-prs.c:1246
+#: src/expr-prs.c:1247
 #, c-format
 msgid "There is no function named %s."
 msgstr ""
 
 #, c-format
 msgid "There is no function named %s."
 msgstr ""
 
-#: src/expr-prs.c:1251
+#: src/expr-prs.c:1252
 #, c-format
 msgid "Function %s may not be given a minimum number of arguments."
 msgstr ""
 
 #, c-format
 msgid "Function %s may not be given a minimum number of arguments."
 msgstr ""
 
-#: src/expr-prs.c:1260
+#: src/expr-prs.c:1261
 #, c-format
 msgid "expecting `)' after %s function"
 msgstr ""
 
 #, c-format
 msgid "expecting `)' after %s function"
 msgstr ""
 
-#: src/expr-prs.c:1282
+#: src/expr-prs.c:1283
 msgid "Boolean"
 msgstr ""
 
 msgid "Boolean"
 msgstr ""
 
-#: src/filename.c:221
-#, c-format
-msgid "Searching for `%s'..."
-msgstr ""
-
-#: src/filename.c:229 src/filename.c:261
-msgid "Search unsuccessful!"
-msgstr ""
-
-#: src/filename.c:254
-#, c-format
-msgid "Found `%s'."
-msgstr ""
-
-#: src/filename.c:686
-#, c-format
-msgid "Not opening pipe file `%s' because SAFER option set."
-msgstr ""
-
-#: src/file-type.c:127
+#: src/file-type.c:129
 msgid "MIXED, GROUPED, or NESTED expected."
 msgstr ""
 
 msgid "MIXED, GROUPED, or NESTED expected."
 msgstr ""
 
-#: src/file-type.c:150
+#: src/file-type.c:152
 msgid "The CASE subcommand is not valid on FILE TYPE MIXED."
 msgstr ""
 
 msgid "The CASE subcommand is not valid on FILE TYPE MIXED."
 msgstr ""
 
-#: src/file-type.c:168
+#: src/file-type.c:170
 msgid "WARN or NOWARN expected after WILD."
 msgstr ""
 
 msgid "WARN or NOWARN expected after WILD."
 msgstr ""
 
-#: src/file-type.c:176
+#: src/file-type.c:178
 msgid "The DUPLICATE subcommand is not valid on FILE TYPE MIXED."
 msgstr ""
 
 msgid "The DUPLICATE subcommand is not valid on FILE TYPE MIXED."
 msgstr ""
 
-#: src/file-type.c:190
+#: src/file-type.c:192
 msgid "DUPLICATE=CASE is only valid on FILE TYPE NESTED."
 msgstr ""
 
 msgid "DUPLICATE=CASE is only valid on FILE TYPE NESTED."
 msgstr ""
 
-#: src/file-type.c:199
+#: src/file-type.c:201
 #, c-format
 msgid "WARN%s expected after DUPLICATE."
 msgstr ""
 
 #, c-format
 msgid "WARN%s expected after DUPLICATE."
 msgstr ""
 
-#: src/file-type.c:200
+#: src/file-type.c:202
 msgid ", NOWARN, or CASE"
 msgstr ""
 
 msgid ", NOWARN, or CASE"
 msgstr ""
 
-#: src/file-type.c:201
+#: src/file-type.c:203
 msgid " or NOWARN"
 msgstr ""
 
 msgid " or NOWARN"
 msgstr ""
 
-#: src/file-type.c:209
+#: src/file-type.c:211
 msgid "The MISSING subcommand is not valid on FILE TYPE MIXED."
 msgstr ""
 
 msgid "The MISSING subcommand is not valid on FILE TYPE MIXED."
 msgstr ""
 
-#: src/file-type.c:221
+#: src/file-type.c:223
 msgid "WARN or NOWARN after MISSING."
 msgstr ""
 
 msgid "WARN or NOWARN after MISSING."
 msgstr ""
 
-#: src/file-type.c:229
+#: src/file-type.c:231
 msgid "ORDERED is only valid on FILE TYPE GROUPED."
 msgstr ""
 
 msgid "ORDERED is only valid on FILE TYPE GROUPED."
 msgstr ""
 
-#: src/file-type.c:240
+#: src/file-type.c:242
 msgid "YES or NO expected after ORDERED."
 msgstr ""
 
 msgid "YES or NO expected after ORDERED."
 msgstr ""
 
-#: src/file-type.c:246 src/file-type.c:540 src/get.c:335
+#: src/file-type.c:248 src/file-type.c:543 src/get.c:389
 msgid "while expecting a valid subcommand"
 msgstr ""
 
 msgid "while expecting a valid subcommand"
 msgstr ""
 
-#: src/file-type.c:253
+#: src/file-type.c:255
 msgid "The required RECORD subcommand was not present."
 msgstr ""
 
 msgid "The required RECORD subcommand was not present."
 msgstr ""
 
-#: src/file-type.c:261
+#: src/file-type.c:263
 msgid "The required CASE subcommand was not present."
 msgstr ""
 
 msgid "The required CASE subcommand was not present."
 msgstr ""
 
-#: src/file-type.c:267
+#: src/file-type.c:269
 msgid "CASE and RECORD must specify different variable names."
 msgstr ""
 
 msgid "CASE and RECORD must specify different variable names."
 msgstr ""
 
-#: src/file-type.c:324
+#: src/file-type.c:327
 msgid "Column value must be positive."
 msgstr ""
 
 msgid "Column value must be positive."
 msgstr ""
 
-#: src/file-type.c:340
+#: src/file-type.c:343
 msgid "Ending column precedes beginning column."
 msgstr ""
 
 msgid "Ending column precedes beginning column."
 msgstr ""
 
-#: src/file-type.c:360
+#: src/file-type.c:363
 msgid "Bad format specifier name."
 msgstr ""
 
 msgid "Bad format specifier name."
 msgstr ""
 
-#: src/file-type.c:389 src/file-type.c:577
+#: src/file-type.c:392 src/file-type.c:580
 msgid ""
 "This command may only appear within a FILE TYPE/END FILE TYPE structure."
 msgstr ""
 
 msgid ""
 "This command may only appear within a FILE TYPE/END FILE TYPE structure."
 msgstr ""
 
-#: src/file-type.c:412
+#: src/file-type.c:415
 msgid "OTHER may appear only on the last RECORD TYPE command."
 msgstr ""
 
 msgid "OTHER may appear only on the last RECORD TYPE command."
 msgstr ""
 
-#: src/file-type.c:422
+#: src/file-type.c:425
 msgid "No input commands (DATA LIST, REPEATING DATA) for above RECORD TYPE."
 msgstr ""
 
 msgid "No input commands (DATA LIST, REPEATING DATA) for above RECORD TYPE."
 msgstr ""
 
-#: src/file-type.c:473
+#: src/file-type.c:476
 msgid ""
 "The CASE subcommand is not allowed on the RECORD TYPE command for FILE TYPE "
 "MIXED."
 msgstr ""
 
 msgid ""
 "The CASE subcommand is not allowed on the RECORD TYPE command for FILE TYPE "
 "MIXED."
 msgstr ""
 
-#: src/file-type.c:483
+#: src/file-type.c:486
 msgid ""
 "No variable name may be specified for the CASE subcommand on RECORD TYPE."
 msgstr ""
 
 msgid ""
 "No variable name may be specified for the CASE subcommand on RECORD TYPE."
 msgstr ""
 
-#: src/file-type.c:491
+#: src/file-type.c:494
 msgid ""
 "The CASE column specification on RECORD TYPE must give a format specifier "
 "that is the same type as that of the CASE column specification given on FILE "
 "TYPE."
 msgstr ""
 
 msgid ""
 "The CASE column specification on RECORD TYPE must give a format specifier "
 "that is the same type as that of the CASE column specification given on FILE "
 "TYPE."
 msgstr ""
 
-#: src/file-type.c:507
+#: src/file-type.c:510
 msgid "WARN or NOWARN expected on DUPLICATE subcommand."
 msgstr ""
 
 msgid "WARN or NOWARN expected on DUPLICATE subcommand."
 msgstr ""
 
-#: src/file-type.c:521
+#: src/file-type.c:524
 msgid "WARN or NOWARN expected on MISSING subcommand."
 msgstr ""
 
 msgid "WARN or NOWARN expected on MISSING subcommand."
 msgstr ""
 
-#: src/file-type.c:534
+#: src/file-type.c:537
 msgid "YES or NO expected on SPREAD subcommand."
 msgstr ""
 
 msgid "YES or NO expected on SPREAD subcommand."
 msgstr ""
 
-#: src/file-type.c:590
+#: src/file-type.c:593
 msgid "No input commands (DATA LIST, REPEATING DATA) on above RECORD TYPE."
 msgstr ""
 
 msgid "No input commands (DATA LIST, REPEATING DATA) on above RECORD TYPE."
 msgstr ""
 
-#: src/file-type.c:597
+#: src/file-type.c:600
 msgid "No commands between FILE TYPE and END FILE TYPE."
 msgstr ""
 
 msgid "No commands between FILE TYPE and END FILE TYPE."
 msgstr ""
 
-#: src/file-type.c:666
+#: src/file-type.c:669
 #, c-format
 msgid "Unknown record type \"%.*s\"."
 msgstr ""
 
 #, c-format
 msgid "Unknown record type \"%.*s\"."
 msgstr ""
 
-#: src/file-type.c:690
+#: src/file-type.c:693
 #, c-format
 msgid "Unknown record type %g."
 msgstr ""
 
 #, c-format
 msgid "Unknown record type %g."
 msgstr ""
 
-#: src/flip.c:77
+#: src/filename.c:221
+#, c-format
+msgid "Searching for `%s'..."
+msgstr ""
+
+#: src/filename.c:229 src/filename.c:261
+msgid "Search unsuccessful!"
+msgstr ""
+
+#: src/filename.c:254
+#, c-format
+msgid "Found `%s'."
+msgstr ""
+
+#: src/filename.c:686
+#, c-format
+msgid "Not opening pipe file `%s' because SAFER option set."
+msgstr ""
+
+#: src/flip.c:78
 msgid ""
 "FLIP ignores TEMPORARY.  Temporary transformations will be made permanent."
 msgstr ""
 
 msgid ""
 "FLIP ignores TEMPORARY.  Temporary transformations will be made permanent."
 msgstr ""
 
-#: src/flip.c:217
+#: src/flip.c:218
 #, c-format
 msgid "Could not create acceptable variant for variable %s."
 msgstr ""
 
 #, c-format
 msgid "Could not create acceptable variant for variable %s."
 msgstr ""
 
-#: src/flip.c:233
+#: src/flip.c:234
 msgid "Cannot create more than 99999 variable names."
 msgstr ""
 
 msgid "Cannot create more than 99999 variable names."
 msgstr ""
 
-#: src/flip.c:277
+#: src/flip.c:278
 msgid "Could not create temporary file for FLIP."
 msgstr ""
 
 msgid "Could not create temporary file for FLIP."
 msgstr ""
 
-#: src/flip.c:284 src/flip.c:352
+#: src/flip.c:285 src/flip.c:353
 #, c-format
 msgid "Error writing FLIP file: %s."
 msgstr ""
 
 #, c-format
 msgid "Error writing FLIP file: %s."
 msgstr ""
 
-#: src/flip.c:394
+#: src/flip.c:395
 #, c-format
 msgid "Error rewinding FLIP file: %s."
 msgstr ""
 
 #, c-format
 msgid "Error rewinding FLIP file: %s."
 msgstr ""
 
-#: src/flip.c:398
+#: src/flip.c:399
 msgid "Error creating FLIP source file."
 msgstr ""
 
 msgid "Error creating FLIP source file."
 msgstr ""
 
-#: src/flip.c:407
+#: src/flip.c:408
 #, c-format
 msgid "Error reading FLIP file: %s."
 msgstr ""
 
 #, c-format
 msgid "Error reading FLIP file: %s."
 msgstr ""
 
-#: src/flip.c:424
+#: src/flip.c:425
 #, c-format
 msgid "Error seeking FLIP source file: %s."
 msgstr ""
 
 #, c-format
 msgid "Error seeking FLIP source file: %s."
 msgstr ""
 
-#: src/flip.c:429
+#: src/flip.c:430
 #, c-format
 msgid "Error writing FLIP source file: %s."
 msgstr ""
 
 #, c-format
 msgid "Error writing FLIP source file: %s."
 msgstr ""
 
-#: src/flip.c:440
+#: src/flip.c:441
 #, c-format
 msgid "Error rewind FLIP source file: %s."
 msgstr ""
 
 #, c-format
 msgid "Error rewind FLIP source file: %s."
 msgstr ""
 
-#: src/flip.c:492
+#: src/flip.c:493
 #, c-format
 msgid "Error reading FLIP temporary file: %s."
 msgstr ""
 
 #, c-format
 msgid "Error reading FLIP temporary file: %s."
 msgstr ""
 
-#: src/flip.c:495
+#: src/flip.c:496
 msgid "Unexpected end of file reading FLIP temporary file."
 msgstr ""
 
 msgid "Unexpected end of file reading FLIP temporary file."
 msgstr ""
 
@@ -1765,15 +1724,15 @@ msgstr ""
 msgid "Format %s may not be assigned to a %s variable."
 msgstr ""
 
 msgid "Format %s may not be assigned to a %s variable."
 msgstr ""
 
-#: src/formats.c:116 src/numeric.c:64 src/numeric.c:135
+#: src/formats.c:116 src/numeric.c:65 src/numeric.c:136
 msgid "`)' expected after output format."
 msgstr ""
 
 msgid "`)' expected after output format."
 msgstr ""
 
-#: src/get.c:341
+#: src/get.c:395
 msgid "All variables deleted from system file dictionary."
 msgstr ""
 
 msgid "All variables deleted from system file dictionary."
 msgstr ""
 
-#: src/get.c:388
+#: src/get.c:445
 #, c-format
 msgid ""
 "Cannot rename %s as %s because there already exists a variable named %s.  To "
 #, c-format
 msgid ""
 "Cannot rename %s as %s because there already exists a variable named %s.  To "
@@ -1781,78 +1740,78 @@ msgid ""
 "as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, \"/RENAME (A B C=B C A)\"."
 msgstr ""
 
 "as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, \"/RENAME (A B C=B C A)\"."
 msgstr ""
 
-#: src/get.c:413
+#: src/get.c:470
 msgid "`=' expected after variable list."
 msgstr ""
 
 msgid "`=' expected after variable list."
 msgstr ""
 
-#: src/get.c:420
+#: src/get.c:477
 #, 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 ""
 
 #, 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:433
+#: src/get.c:490
 #, c-format
 msgid "Requested renaming duplicates variable name %s."
 msgstr ""
 
 #, c-format
 msgid "Requested renaming duplicates variable name %s."
 msgstr ""
 
-#: src/get.c:564
+#: src/get.c:674
 msgid "The BY subcommand may be given once at most."
 msgstr ""
 
 msgid "The BY subcommand may be given once at most."
 msgstr ""
 
-#: src/get.c:631
+#: src/get.c:746
 msgid "The active file may not be specified more than once."
 msgstr ""
 
 msgid "The active file may not be specified more than once."
 msgstr ""
 
-#: src/get.c:640
+#: src/get.c:755
 msgid "Cannot specify the active file since no active file has been defined."
 msgstr ""
 
 msgid "Cannot specify the active file since no active file has been defined."
 msgstr ""
 
-#: src/get.c:648
+#: src/get.c:763
 msgid ""
 "MATCH FILES may not be used after TEMPORARY when the active file is an input "
 "source.  Temporary transformations will be made permanent."
 msgstr ""
 
 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:682
+#: src/get.c:793
 msgid ""
 "IN, FIRST, and LAST subcommands may not occur before the first FILE or TABLE."
 msgstr ""
 
 msgid ""
 "IN, FIRST, and LAST subcommands may not occur before the first FILE or TABLE."
 msgstr ""
 
-#: src/get.c:717
+#: src/get.c:828
 #, c-format
 msgid "Multiple %s subcommands for a single FILE or TABLE."
 msgstr ""
 
 #, c-format
 msgid "Multiple %s subcommands for a single FILE or TABLE."
 msgstr ""
 
-#: src/get.c:727
+#: src/get.c:838
 #, c-format
 msgid "Duplicate variable name %s while creating %s variable."
 msgstr ""
 
 #, c-format
 msgid "Duplicate variable name %s while creating %s variable."
 msgstr ""
 
-#: src/get.c:741
+#: src/get.c:850
 msgid ""
 "RENAME, KEEP, and DROP subcommands may not occur before the first FILE or "
 "TABLE."
 msgstr ""
 
 msgid ""
 "RENAME, KEEP, and DROP subcommands may not occur before the first FILE or "
 "TABLE."
 msgstr ""
 
-#: src/get.c:765
+#: src/get.c:877
 msgid "The BY subcommand is required when a TABLE subcommand is given."
 msgstr ""
 
 msgid "The BY subcommand is required when a TABLE subcommand is given."
 msgstr ""
 
-#: src/get.c:786
+#: src/get.c:896
 #, c-format
 msgid "File %s lacks BY variable %s."
 msgstr ""
 
 #, c-format
 msgid "File %s lacks BY variable %s."
 msgstr ""
 
-#: src/get.c:1282
+#: src/get.c:1390
 #, c-format
 msgid ""
 "Variable %s in file %s (%s) has different type or width from the same "
 "variable in earlier file (%s)."
 msgstr ""
 
 #, 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:1330
+#: src/get.c:1468
 msgid "expecting COMM or TAPE"
 msgstr ""
 
 msgid "expecting COMM or TAPE"
 msgstr ""
 
@@ -2073,7 +2032,7 @@ msgstr ""
 msgid "HTML output driver: %s: %s"
 msgstr ""
 
 msgid "HTML output driver: %s: %s"
 msgstr ""
 
-#: src/html.c:403 src/list.q:250
+#: src/html.c:403 src/list.q:252
 #, c-format
 msgid "Cannot open first page on HTML device %s."
 msgstr ""
 #, c-format
 msgid "Cannot open first page on HTML device %s."
 msgstr ""
@@ -2082,27 +2041,27 @@ msgstr ""
 msgid "expecting filename"
 msgstr ""
 
 msgid "expecting filename"
 msgstr ""
 
-#: src/inpt-pgm.c:81
+#: src/inpt-pgm.c:82
 msgid "No matching INPUT PROGRAM command."
 msgstr ""
 
 msgid "No matching INPUT PROGRAM command."
 msgstr ""
 
-#: src/inpt-pgm.c:86
+#: src/inpt-pgm.c:87
 msgid ""
 "No data-input or transformation commands specified between INPUT PROGRAM and "
 "END INPUT PROGRAM."
 msgstr ""
 
 msgid ""
 "No data-input or transformation commands specified between INPUT PROGRAM and "
 "END INPUT PROGRAM."
 msgstr ""
 
-#: src/inpt-pgm.c:287 src/inpt-pgm.c:420
+#: src/inpt-pgm.c:288 src/inpt-pgm.c:418
 msgid ""
 "This command may only be executed between INPUT PROGRAM and END INPUT "
 "PROGRAM."
 msgstr ""
 
 msgid ""
 "This command may only be executed between INPUT PROGRAM and END INPUT "
 "PROGRAM."
 msgstr ""
 
-#: src/inpt-pgm.c:342
+#: src/inpt-pgm.c:338
 msgid "COLUMN subcommand multiply specified."
 msgstr ""
 
 msgid "COLUMN subcommand multiply specified."
 msgstr ""
 
-#: src/inpt-pgm.c:395
+#: src/inpt-pgm.c:391
 msgid ""
 "REREAD: Column numbers must be positive finite numbers.  Column set to 1."
 msgstr ""
 msgid ""
 "REREAD: Column numbers must be positive finite numbers.  Column set to 1."
 msgstr ""
@@ -2166,7 +2125,7 @@ msgstr ""
 msgid "<ERROR>"
 msgstr ""
 
 msgid "<ERROR>"
 msgstr ""
 
-#: src/lexer.c:993 src/pfm-read.c:136 src/repeat.c:213
+#: src/lexer.c:993 src/pfm-read.c:134 src/repeat.c:214
 msgid "Unexpected end of file."
 msgstr ""
 
 msgid "Unexpected end of file."
 msgstr ""
 
@@ -2207,232 +2166,232 @@ msgid ""
 "spaces."
 msgstr ""
 
 "spaces."
 msgstr ""
 
-#: src/loop.c:193
+#: src/loop.c:194
 msgid "The index variable may not be a string variable."
 msgstr ""
 
 msgid "The index variable may not be a string variable."
 msgstr ""
 
-#: src/loop.c:299
+#: src/loop.c:300
 msgid "There is no LOOP command that corresponds to this END LOOP."
 msgstr ""
 
 msgid "There is no LOOP command that corresponds to this END LOOP."
 msgstr ""
 
-#: src/loop.c:493
+#: src/loop.c:494
 msgid ""
 "This command may only appear enclosed in a LOOP/END LOOP control structure."
 msgstr ""
 
 msgid ""
 "This command may only appear enclosed in a LOOP/END LOOP control structure."
 msgstr ""
 
-#: src/loop.c:499
+#: src/loop.c:500
 msgid "BREAK not enclosed in DO IF structure."
 msgstr ""
 
 msgid "BREAK not enclosed in DO IF structure."
 msgstr ""
 
-#: src/loop.c:577
+#: src/loop.c:578
 #, c-format
 msgid "%s without %s."
 msgstr ""
 
 #, c-format
 msgid "%s without %s."
 msgstr ""
 
-#: src/main.c:74
+#: src/main.c:76
 msgid "Error initializing output drivers."
 msgstr ""
 
 msgid "Error initializing output drivers."
 msgstr ""
 
-#: src/main.c:140
+#: src/main.c:146
 msgid "This command not executed."
 msgstr ""
 
 msgid "This command not executed."
 msgstr ""
 
-#: src/main.c:144
+#: src/main.c:150
 msgid ""
 "Skipping the rest of this command.  Part of this command may have been "
 "executed."
 msgstr ""
 
 msgid ""
 "Skipping the rest of this command.  Part of this command may have been "
 "executed."
 msgstr ""
 
-#: src/main.c:149
+#: src/main.c:155
 msgid ""
 "Skipping the rest of this command.  This command was fully executed up to "
 "this point."
 msgstr ""
 
 msgid ""
 "Skipping the rest of this command.  This command was fully executed up to "
 "this point."
 msgstr ""
 
-#: src/main.c:154
+#: src/main.c:160
 msgid ""
 "Trailing garbage was encountered following this command.  The command was "
 "fully executed to this point."
 msgstr ""
 
 msgid ""
 "Trailing garbage was encountered following this command.  The command was "
 "fully executed to this point."
 msgstr ""
 
-#: src/main.c:171
+#: src/main.c:177
 msgid "The rest of this command has been discarded."
 msgstr ""
 
 msgid "The rest of this command has been discarded."
 msgstr ""
 
-#: src/matrix-data.c:185
+#: src/matrix-data.c:208
 msgid "VARIABLES subcommand multiply specified."
 msgstr ""
 
 msgid "VARIABLES subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:200
+#: src/matrix-data.c:223
 msgid "VARNAME_ cannot be explicitly specified on VARIABLES."
 msgstr ""
 
 msgid "VARNAME_ cannot be explicitly specified on VARIABLES."
 msgstr ""
 
-#: src/matrix-data.c:265
+#: src/matrix-data.c:284
 msgid "in FORMAT subcommand"
 msgstr ""
 
 msgid "in FORMAT subcommand"
 msgstr ""
 
-#: src/matrix-data.c:276
+#: src/matrix-data.c:295
 msgid "SPLIT subcommand multiply specified."
 msgstr ""
 
 msgid "SPLIT subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:283
+#: src/matrix-data.c:302
 msgid "in SPLIT subcommand"
 msgstr ""
 
 msgid "in SPLIT subcommand"
 msgstr ""
 
-#: src/matrix-data.c:292
+#: src/matrix-data.c:311
 msgid "Split variable may not be named ROWTYPE_ or VARNAME_."
 msgstr ""
 
 msgid "Split variable may not be named ROWTYPE_ or VARNAME_."
 msgstr ""
 
-#: src/matrix-data.c:325
+#: src/matrix-data.c:345
 #, c-format
 msgid "Split variable %s is already another type."
 msgstr ""
 
 #, c-format
 msgid "Split variable %s is already another type."
 msgstr ""
 
-#: src/matrix-data.c:340
+#: src/matrix-data.c:360
 msgid "FACTORS subcommand multiply specified."
 msgstr ""
 
 msgid "FACTORS subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:355
+#: src/matrix-data.c:378
 #, c-format
 msgid "Factor variable %s is already another type."
 msgstr ""
 
 #, c-format
 msgid "Factor variable %s is already another type."
 msgstr ""
 
-#: src/matrix-data.c:370
+#: src/matrix-data.c:393
 msgid "CELLS subcommand multiply specified."
 msgstr ""
 
 msgid "CELLS subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:376 src/matrix-data.c:395
+#: src/matrix-data.c:399 src/matrix-data.c:418
 msgid "expecting positive integer"
 msgstr ""
 
 msgid "expecting positive integer"
 msgstr ""
 
-#: src/matrix-data.c:389
+#: src/matrix-data.c:412
 msgid "N subcommand multiply specified."
 msgstr ""
 
 msgid "N subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:410
+#: src/matrix-data.c:433
 msgid "CONTENTS subcommand multiply specified."
 msgstr ""
 
 msgid "CONTENTS subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:430
+#: src/matrix-data.c:453
 msgid "Nested parentheses not allowed."
 msgstr ""
 
 msgid "Nested parentheses not allowed."
 msgstr ""
 
-#: src/matrix-data.c:440
+#: src/matrix-data.c:463
 msgid "Mismatched right parenthesis (`(')."
 msgstr ""
 
 msgid "Mismatched right parenthesis (`(')."
 msgstr ""
 
-#: src/matrix-data.c:445
+#: src/matrix-data.c:468
 msgid "Empty parentheses not allowed."
 msgstr ""
 
 msgid "Empty parentheses not allowed."
 msgstr ""
 
-#: src/matrix-data.c:458 src/matrix-data.c:466
+#: src/matrix-data.c:481 src/matrix-data.c:489
 msgid "in CONTENTS subcommand"
 msgstr ""
 
 msgid "in CONTENTS subcommand"
 msgstr ""
 
-#: src/matrix-data.c:473
+#: src/matrix-data.c:496
 #, c-format
 msgid "Content multiply specified for %s."
 msgstr ""
 
 #, c-format
 msgid "Content multiply specified for %s."
 msgstr ""
 
-#: src/matrix-data.c:490
+#: src/matrix-data.c:513
 msgid "Missing right parenthesis."
 msgstr ""
 
 msgid "Missing right parenthesis."
 msgstr ""
 
-#: src/matrix-data.c:510
+#: src/matrix-data.c:533
 msgid "Missing VARIABLES subcommand."
 msgstr ""
 
 msgid "Missing VARIABLES subcommand."
 msgstr ""
 
-#: src/matrix-data.c:516
+#: src/matrix-data.c:539
 msgid ""
 "CONTENTS subcommand not specified: assuming file contains only CORR matrix."
 msgstr ""
 
 msgid ""
 "CONTENTS subcommand not specified: assuming file contains only CORR matrix."
 msgstr ""
 
-#: src/matrix-data.c:526
+#: src/matrix-data.c:549
 msgid ""
 "Missing CELLS subcommand.  CELLS is required when ROWTYPE_ is not given in "
 "the data and factors are present."
 msgstr ""
 
 msgid ""
 "Missing CELLS subcommand.  CELLS is required when ROWTYPE_ is not given in "
 "the data and factors are present."
 msgstr ""
 
-#: src/matrix-data.c:534
+#: src/matrix-data.c:557
 msgid "Split file values must be present in the data when ROWTYPE_ is present."
 msgstr ""
 
 msgid "Split file values must be present in the data when ROWTYPE_ is present."
 msgstr ""
 
-#: src/matrix-data.c:589
+#: src/matrix-data.c:610
 msgid "No continuous variables specified."
 msgstr ""
 
 msgid "No continuous variables specified."
 msgstr ""
 
-#: src/matrix-data.c:815
+#: src/matrix-data.c:853
 msgid "Scope of string exceeds line."
 msgstr ""
 
 msgid "Scope of string exceeds line."
 msgstr ""
 
-#: src/matrix-data.c:882
+#: src/matrix-data.c:920
 #, c-format
 msgid "End of line expected %s while reading %s."
 msgstr ""
 
 #, c-format
 msgid "End of line expected %s while reading %s."
 msgstr ""
 
-#: src/matrix-data.c:1070
+#: src/matrix-data.c:1106
 #, c-format
 msgid "expecting value for %s %s"
 msgstr ""
 
 #, c-format
 msgid "expecting value for %s %s"
 msgstr ""
 
-#: src/matrix-data.c:1232
+#: src/matrix-data.c:1270
 #, c-format
 msgid "Syntax error expecting SPLIT FILE value %s."
 msgstr ""
 
 #, c-format
 msgid "Syntax error expecting SPLIT FILE value %s."
 msgstr ""
 
-#: src/matrix-data.c:1241
+#: src/matrix-data.c:1279
 #, c-format
 msgid "Expecting value %g for %s."
 msgstr ""
 
 #, c-format
 msgid "Expecting value %g for %s."
 msgstr ""
 
-#: src/matrix-data.c:1282 src/matrix-data.c:1750
+#: src/matrix-data.c:1320 src/matrix-data.c:1787
 #, c-format
 msgid "Syntax error expecting factor value %s."
 msgstr ""
 
 #, c-format
 msgid "Syntax error expecting factor value %s."
 msgstr ""
 
-#: src/matrix-data.c:1291
+#: src/matrix-data.c:1329
 #, c-format
 msgid "Syntax error expecting value %g for %s %s."
 msgstr ""
 
 #, c-format
 msgid "Syntax error expecting value %g for %s %s."
 msgstr ""
 
-#: src/matrix-data.c:1527
+#: src/matrix-data.c:1564
 #, c-format
 msgid "Syntax error %s expecting SPLIT FILE value."
 msgstr ""
 
 #, c-format
 msgid "Syntax error %s expecting SPLIT FILE value."
 msgstr ""
 
-#: src/matrix-data.c:1657
+#: src/matrix-data.c:1694
 #, c-format
 msgid ""
 "Expected %d lines of data for %s content; actually saw %d lines.  No data "
 "will be output for this content."
 msgstr ""
 
 #, c-format
 msgid ""
 "Expected %d lines of data for %s content; actually saw %d lines.  No data "
 "will be output for this content."
 msgstr ""
 
-#: src/matrix-data.c:1692
+#: src/matrix-data.c:1729
 #, c-format
 msgid "Multiply specified ROWTYPE_ %s."
 msgstr ""
 
 #, c-format
 msgid "Multiply specified ROWTYPE_ %s."
 msgstr ""
 
-#: src/matrix-data.c:1697
+#: src/matrix-data.c:1734
 #, c-format
 msgid "Syntax error %s expecting ROWTYPE_ string."
 msgstr ""
 
 #, c-format
 msgid "Syntax error %s expecting ROWTYPE_ string."
 msgstr ""
 
-#: src/matrix-data.c:1717
+#: src/matrix-data.c:1754
 #, c-format
 msgid "Syntax error %s."
 msgstr ""
 
 #, c-format
 msgid "Syntax error %s."
 msgstr ""
 
-#: src/matrix-data.c:1867
+#: src/matrix-data.c:1904
 #, c-format
 msgid "Duplicate specification for %s."
 msgstr ""
 
 #, c-format
 msgid "Duplicate specification for %s."
 msgstr ""
 
-#: src/matrix-data.c:1879
+#: src/matrix-data.c:1916
 #, c-format
 msgid "Too many rows of matrix data for %s."
 msgstr ""
 
 #, c-format
 msgid "Too many rows of matrix data for %s."
 msgstr ""
 
-#: src/matrix-data.c:1927
+#: src/matrix-data.c:1964
 #, c-format
 msgid "Syntax error expecting value for %s %s."
 msgstr ""
 #, c-format
 msgid "Syntax error expecting value for %s %s."
 msgstr ""
@@ -2475,79 +2434,79 @@ msgstr ""
 msgid "String is not of proper length."
 msgstr ""
 
 msgid "String is not of proper length."
 msgstr ""
 
-#: src/mis-val.c:316 src/repeat.c:459
+#: src/mis-val.c:316 src/repeat.c:460
 msgid "String expected."
 msgstr ""
 
 msgid "String expected."
 msgstr ""
 
-#: src/modify-vars.c:88
+#: src/modify-vars.c:89
 msgid ""
 "MODIFY VARS may not be used after TEMPORARY.  Temporary transformations will "
 "be made permanent."
 msgstr ""
 
 msgid ""
 "MODIFY VARS may not be used after TEMPORARY.  Temporary transformations will "
 "be made permanent."
 msgstr ""
 
-#: src/modify-vars.c:112
+#: src/modify-vars.c:113
 msgid "REORDER subcommand may be given at most once."
 msgstr ""
 
 msgid "REORDER subcommand may be given at most once."
 msgstr ""
 
-#: src/modify-vars.c:135
+#: src/modify-vars.c:136
 msgid "Cannot specify ALL after specifying a set of variables."
 msgstr ""
 
 msgid "Cannot specify ALL after specifying a set of variables."
 msgstr ""
 
-#: src/modify-vars.c:145
+#: src/modify-vars.c:146
 msgid "`(' expected on REORDER subcommand."
 msgstr ""
 
 msgid "`(' expected on REORDER subcommand."
 msgstr ""
 
-#: src/modify-vars.c:157
+#: src/modify-vars.c:158
 msgid "`)' expected following variable names on REORDER subcommand."
 msgstr ""
 
 msgid "`)' expected following variable names on REORDER subcommand."
 msgstr ""
 
-#: src/modify-vars.c:175
+#: src/modify-vars.c:176
 msgid "RENAME subcommand may be given at most once."
 msgstr ""
 
 msgid "RENAME subcommand may be given at most once."
 msgstr ""
 
-#: src/modify-vars.c:188
+#: src/modify-vars.c:189
 msgid "`(' expected on RENAME subcommand."
 msgstr ""
 
 msgid "`(' expected on RENAME subcommand."
 msgstr ""
 
-#: src/modify-vars.c:196
+#: src/modify-vars.c:197
 msgid ""
 "`=' expected between lists of new and old variable names on RENAME "
 "subcommand."
 msgstr ""
 
 msgid ""
 "`=' expected between lists of new and old variable names on RENAME "
 "subcommand."
 msgstr ""
 
-#: src/modify-vars.c:204 src/rename-vars.c:74
+#: src/modify-vars.c:205 src/rename-vars.c:75
 #, c-format
 msgid ""
 "Differing number of variables in old name list (%d) and in new name list (%"
 "d)."
 msgstr ""
 
 #, c-format
 msgid ""
 "Differing number of variables in old name list (%d) and in new name list (%"
 "d)."
 msgstr ""
 
-#: src/modify-vars.c:215
+#: src/modify-vars.c:216
 msgid "`)' expected after variable lists on RENAME subcommand."
 msgstr ""
 
 msgid "`)' expected after variable lists on RENAME subcommand."
 msgstr ""
 
-#: src/modify-vars.c:229
+#: src/modify-vars.c:230
 msgid ""
 "KEEP subcommand may be given at most once.  It may notbe given in "
 "conjunction with the DROP subcommand."
 msgstr ""
 
 msgid ""
 "KEEP subcommand may be given at most once.  It may notbe given in "
 "conjunction with the DROP subcommand."
 msgstr ""
 
-#: src/modify-vars.c:271
+#: src/modify-vars.c:272
 msgid ""
 "DROP subcommand may be given at most once.  It may not be given in "
 "conjunction with the KEEP subcommand."
 msgstr ""
 
 msgid ""
 "DROP subcommand may be given at most once.  It may not be given in "
 "conjunction with the KEEP subcommand."
 msgstr ""
 
-#: src/modify-vars.c:297
+#: src/modify-vars.c:298
 #, c-format
 msgid "Unrecognized subcommand name `%s'."
 msgstr ""
 
 #, c-format
 msgid "Unrecognized subcommand name `%s'."
 msgstr ""
 
-#: src/modify-vars.c:299
+#: src/modify-vars.c:300
 msgid "Subcommand name expected."
 msgstr ""
 
 msgid "Subcommand name expected."
 msgstr ""
 
-#: src/modify-vars.c:307
+#: src/modify-vars.c:308
 msgid "`/' or `.' expected."
 msgstr ""
 
 msgid "`/' or `.' expected."
 msgstr ""
 
@@ -2555,17 +2514,17 @@ msgstr ""
 msgid "expecting weight value"
 msgstr ""
 
 msgid "expecting weight value"
 msgstr ""
 
-#: src/numeric.c:57
+#: src/numeric.c:58
 #, c-format
 msgid "Format type %s may not be used with a numeric variable."
 msgstr ""
 
 #, c-format
 msgid "Format type %s may not be used with a numeric variable."
 msgstr ""
 
-#: src/numeric.c:76 src/numeric.c:158 src/vector.c:155
+#: src/numeric.c:77 src/numeric.c:159 src/vector.c:156
 #, c-format
 msgid "There is already a variable named %s."
 msgstr ""
 
 #, c-format
 msgid "There is already a variable named %s."
 msgstr ""
 
-#: src/numeric.c:128
+#: src/numeric.c:129
 #, c-format
 msgid "Format type %s may not be used with a string variable."
 msgstr ""
 #, c-format
 msgid "Format type %s may not be used with a string variable."
 msgstr ""
@@ -2750,223 +2709,179 @@ msgstr ""
 msgid "portable file %s corrupt at offset %ld: "
 msgstr ""
 
 msgid "portable file %s corrupt at offset %ld: "
 msgstr ""
 
-#: src/pfm-read.c:111 src/pfm-write.c:504
+#: src/pfm-read.c:114 src/pfm-write.c:490
 #, c-format
 msgid "%s: Closing portable file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Closing portable file: %s."
 msgstr ""
 
-#: src/pfm-read.c:144
+#: src/pfm-read.c:142
 msgid "Bad line end."
 msgstr ""
 
 msgid "Bad line end."
 msgstr ""
 
-#: src/pfm-read.c:225
-#, c-format
-msgid "Cannot read file %s as portable file: already opened for %s."
-msgstr ""
-
-#: src/pfm-read.c:231
-#, c-format
-msgid "%s: Opening portable-file handle %s for reading."
-msgstr ""
-
-#: src/pfm-read.c:239
+#: src/pfm-read.c:233
 #, c-format
 msgid ""
 "An error occurred while opening \"%s\" for reading as a portable file: %s."
 msgstr ""
 
 #, c-format
 msgid ""
 "An error occurred while opening \"%s\" for reading as a portable file: %s."
 msgstr ""
 
-#: src/pfm-read.c:274
+#: src/pfm-read.c:256
 msgid "Data record expected."
 msgstr ""
 
 msgid "Data record expected."
 msgstr ""
 
-#: src/pfm-read.c:276
-msgid "Read portable-file dictionary successfully."
-msgstr ""
-
-#: src/pfm-read.c:282
-msgid "Error reading portable-file dictionary."
-msgstr ""
-
-#: src/pfm-read.c:380
+#: src/pfm-read.c:353
 msgid "Missing numeric terminator."
 msgstr ""
 
 msgid "Missing numeric terminator."
 msgstr ""
 
-#: src/pfm-read.c:417
+#: src/pfm-read.c:390
 msgid "Bad integer format."
 msgstr ""
 
 msgid "Bad integer format."
 msgstr ""
 
-#: src/pfm-read.c:447
+#: src/pfm-read.c:419
 #, c-format
 msgid "Bad string length %d."
 msgstr ""
 
 #, c-format
 msgid "Bad string length %d."
 msgstr ""
 
-#: src/pfm-read.c:546
+#: src/pfm-read.c:514
 #, c-format
 msgid "Bad date string length %d."
 msgstr ""
 
 #, c-format
 msgid "Bad date string length %d."
 msgstr ""
 
-#: src/pfm-read.c:550
+#: src/pfm-read.c:518
 msgid "Bad character in date."
 msgstr ""
 
 msgid "Bad character in date."
 msgstr ""
 
-#: src/pfm-read.c:570
+#: src/pfm-read.c:538
 #, c-format
 msgid "Bad time string length %d."
 msgstr ""
 
 #, c-format
 msgid "Bad time string length %d."
 msgstr ""
 
-#: src/pfm-read.c:574
+#: src/pfm-read.c:542
 msgid "Bad character in time."
 msgstr ""
 
 msgid "Bad character in time."
 msgstr ""
 
-#: src/pfm-read.c:624 src/pfm-read.c:631 src/sfm-read.c:912 src/sfm-read.c:920
+#: src/pfm-read.c:592 src/pfm-read.c:599 src/sfm-read.c:873 src/sfm-read.c:881
 #, c-format
 msgid "%s: Bad format specifier byte (%d)."
 msgstr ""
 
 #, c-format
 msgid "%s: Bad format specifier byte (%d)."
 msgstr ""
 
-#: src/pfm-read.c:633
+#: src/pfm-read.c:601
 #, c-format
 msgid "%s variable %s has %s format specifier %s."
 msgstr ""
 
 #, c-format
 msgid "%s variable %s has %s format specifier %s."
 msgstr ""
 
-#: src/pfm-read.c:634 src/print.c:601 src/sfm-read.c:925
+#: src/pfm-read.c:602 src/print.c:607 src/sfm-read.c:886
 msgid "String"
 msgstr ""
 
 msgid "String"
 msgstr ""
 
-#: src/pfm-read.c:634 src/print.c:601 src/sfm-read.c:925
+#: src/pfm-read.c:602 src/print.c:607 src/sfm-read.c:886
 msgid "Numeric"
 msgstr ""
 
 msgid "Numeric"
 msgstr ""
 
-#: src/pfm-read.c:673
+#: src/pfm-read.c:640
 msgid "Expected variable count record."
 msgstr ""
 
 msgid "Expected variable count record."
 msgstr ""
 
-#: src/pfm-read.c:677
+#: src/pfm-read.c:644
 #, c-format
 msgid "Invalid number of variables %d."
 msgstr ""
 
 #, c-format
 msgid "Invalid number of variables %d."
 msgstr ""
 
-#: src/pfm-read.c:687
+#: src/pfm-read.c:654
 #, c-format
 msgid "Unexpected flag value %d."
 msgstr ""
 
 #, c-format
 msgid "Unexpected flag value %d."
 msgstr ""
 
-#: src/pfm-read.c:701
+#: src/pfm-read.c:666
 #, c-format
 msgid "Weight variable name (%s) truncated."
 msgstr ""
 
 #, c-format
 msgid "Weight variable name (%s) truncated."
 msgstr ""
 
-#: src/pfm-read.c:716
+#: src/pfm-read.c:681
 msgid "Expected variable record."
 msgstr ""
 
 msgid "Expected variable record."
 msgstr ""
 
-#: src/pfm-read.c:722
+#: src/pfm-read.c:687
 #, c-format
 msgid "Invalid variable width %d."
 msgstr ""
 
 #, c-format
 msgid "Invalid variable width %d."
 msgstr ""
 
-#: src/pfm-read.c:740
+#: src/pfm-read.c:705
 #, c-format
 msgid "position %d: Variable name has %u characters."
 msgstr ""
 
 #, c-format
 msgid "position %d: Variable name has %u characters."
 msgstr ""
 
-#: src/pfm-read.c:744
+#: src/pfm-read.c:709
 #, c-format
 msgid "position %d: Variable name begins with invalid character."
 msgstr ""
 
 #, c-format
 msgid "position %d: Variable name begins with invalid character."
 msgstr ""
 
-#: src/pfm-read.c:748
+#: src/pfm-read.c:713
 #, c-format
 msgid "position %d: Variable name begins with lowercase letter %c."
 msgstr ""
 
 #, c-format
 msgid "position %d: Variable name begins with lowercase letter %c."
 msgstr ""
 
-#: src/pfm-read.c:761
+#: src/pfm-read.c:726
 #, c-format
 msgid "position %d: Variable name character %d is lowercase letter %c."
 msgstr ""
 
 #, c-format
 msgid "position %d: Variable name character %d is lowercase letter %c."
 msgstr ""
 
-#: src/pfm-read.c:771
+#: src/pfm-read.c:736
 #, c-format
 msgid "position %d: character `\\%03o' is not valid in a variable name."
 msgstr ""
 
 #, c-format
 msgid "position %d: character `\\%03o' is not valid in a variable name."
 msgstr ""
 
-#: src/pfm-read.c:782
+#: src/pfm-read.c:748
 #, c-format
 msgid "Duplicate variable name %s."
 msgstr ""
 
 #, c-format
 msgid "Duplicate variable name %s."
 msgstr ""
 
-#: src/pfm-read.c:826
+#: src/pfm-read.c:792
 #, c-format
 msgid "Bad missing values for %s."
 msgstr ""
 
 #, c-format
 msgid "Bad missing values for %s."
 msgstr ""
 
-#: src/pfm-read.c:849
+#: src/pfm-read.c:815
 #, c-format
 msgid "Weighting variable %s not present in dictionary."
 msgstr ""
 
 #, c-format
 msgid "Weighting variable %s not present in dictionary."
 msgstr ""
 
-#: src/pfm-read.c:922
+#: src/pfm-read.c:886
 #, c-format
 msgid "Unknown variable %s while parsing value labels."
 msgstr ""
 
 #, c-format
 msgid "Unknown variable %s while parsing value labels."
 msgstr ""
 
-#: src/pfm-read.c:925
+#: src/pfm-read.c:889
 #, c-format
 msgid ""
 "Cannot assign value labels to %s and %s, which have different variable types "
 "or widths."
 msgstr ""
 
 #, c-format
 msgid ""
 "Cannot assign value labels to %s and %s, which have different variable types "
 "or widths."
 msgstr ""
 
-#: src/pfm-read.c:958
+#: src/pfm-read.c:922
 #, c-format
 msgid "Duplicate label for value %g for variable %s."
 msgstr ""
 
 #, c-format
 msgid "Duplicate label for value %g for variable %s."
 msgstr ""
 
-#: src/pfm-read.c:961
+#: src/pfm-read.c:925
 #, c-format
 msgid "Duplicate label for value `%.*s' for variable %s."
 msgstr ""
 
 #, c-format
 msgid "Duplicate label for value `%.*s' for variable %s."
 msgstr ""
 
-#: src/pfm-read.c:1032
+#: src/pfm-read.c:978
 msgid "End of file midway through case."
 msgstr ""
 
 msgid "End of file midway through case."
 msgstr ""
 
-#: src/pfm-read.c:1042
-msgid "reading as a portable file"
-msgstr ""
-
-#: src/pfm-write.c:71
-#, c-format
-msgid "Cannot write file %s as portable file: already opened for %s."
-msgstr ""
-
-#: src/pfm-write.c:77
-#, c-format
-msgid "%s: Opening portable-file handle %s for writing."
-msgstr ""
-
-#: src/pfm-write.c:87
+#: src/pfm-write.c:92
 #, c-format
 msgid ""
 "An error occurred while opening \"%s\" for writing as a portable file: %s."
 msgstr ""
 
 #, c-format
 msgid ""
 "An error occurred while opening \"%s\" for writing as a portable file: %s."
 msgstr ""
 
-#: src/pfm-write.c:124
-msgid "Wrote portable-file header successfully."
-msgstr ""
-
-#: src/pfm-write.c:129
-msgid "Error writing portable-file header."
-msgstr ""
-
-#: src/pfm-write.c:170
+#: src/pfm-write.c:154
 #, c-format
 msgid "%s: Writing portable file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Writing portable file: %s."
 msgstr ""
 
-#: src/pfm-write.c:514
-msgid "writing as a portable file"
-msgstr ""
-
 #: src/postscript.c:323
 #, c-format
 msgid "PostScript driver initializing as `%s'..."
 #: src/postscript.c:323
 #, c-format
 msgid "PostScript driver initializing as `%s'..."
@@ -3098,80 +3013,80 @@ msgstr ""
 msgid "PostScript driver: Cannot find encoding `%s' for PostScript font `%s'."
 msgstr ""
 
 msgid "PostScript driver: Cannot find encoding `%s' for PostScript font `%s'."
 msgstr ""
 
-#: src/print.c:180
+#: src/print.c:179
 msgid "expecting a valid subcommand"
 msgstr ""
 
 msgid "expecting a valid subcommand"
 msgstr ""
 
-#: src/print.c:359 src/print.c:376
+#: src/print.c:365 src/print.c:382
 #, c-format
 msgid "%g is not a valid column location."
 msgstr ""
 
 #, c-format
 msgid "%g is not a valid column location."
 msgstr ""
 
-#: src/print.c:370
+#: src/print.c:376
 #, c-format
 msgid "Column location expected following `%d-'."
 msgstr ""
 
 #, c-format
 msgid "Column location expected following `%d-'."
 msgstr ""
 
-#: src/print.c:381
+#: src/print.c:387
 #, c-format
 msgid ""
 "%d-%ld is not a valid column range.  The second column must be greater than "
 "or equal to the first."
 msgstr ""
 
 #, c-format
 msgid ""
 "%d-%ld is not a valid column range.  The second column must be greater than "
 "or equal to the first."
 msgstr ""
 
-#: src/print.c:487
+#: src/print.c:493
 #, c-format
 msgid ""
 "%s is not of the same type as %s.  To specify variables of different types "
 "in the same variable list, use a FORTRAN-like format specifier."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s is not of the same type as %s.  To specify variables of different types "
 "in the same variable list, use a FORTRAN-like format specifier."
 msgstr ""
 
-#: src/print.c:517
+#: src/print.c:523
 msgid ""
 "The ending column for a field must not be less than the starting column."
 msgstr ""
 
 msgid ""
 "The ending column for a field must not be less than the starting column."
 msgstr ""
 
-#: src/print.c:600
+#: src/print.c:606
 #, c-format
 msgid "%s variables cannot be displayed with format %s."
 msgstr ""
 
 #, c-format
 msgid "%s variables cannot be displayed with format %s."
 msgstr ""
 
-#: src/print.c:678
+#: src/print.c:684
 msgid ""
 "The number of format specifications exceeds the number of variable names "
 "given."
 msgstr ""
 
 msgid ""
 "The number of format specifications exceeds the number of variable names "
 "given."
 msgstr ""
 
-#: src/print.c:687
+#: src/print.c:693
 #, c-format
 msgid "Display format %s may not be used with a %s variable."
 msgstr ""
 
 #, c-format
 msgid "Display format %s may not be used with a %s variable."
 msgstr ""
 
-#: src/print.c:835
+#: src/print.c:841
 #, c-format
 msgid "Writing %d record(s) to file %s."
 msgstr ""
 
 #, c-format
 msgid "Writing %d record(s) to file %s."
 msgstr ""
 
-#: src/print.c:838
+#: src/print.c:844
 #, c-format
 msgid "Writing %d record(s) to the listing file."
 msgstr ""
 
 #, c-format
 msgid "Writing %d record(s) to the listing file."
 msgstr ""
 
-#: src/print.c:1080
+#: src/print.c:1092
 #, c-format
 msgid ""
 "The expression on PRINT SPACE evaluated to %d.  It's not possible to PRINT "
 "SPACE a negative number of lines."
 msgstr ""
 
 #, c-format
 msgid ""
 "The expression on PRINT SPACE evaluated to %d.  It's not possible to PRINT "
 "SPACE a negative number of lines."
 msgstr ""
 
-#: src/recode.c:282
+#: src/recode.c:283
 #, c-format
 msgid ""
 "%d variable(s) cannot be recoded into %d variable(s).  Specify the same "
 "number of variables as input and output variables."
 msgstr ""
 
 #, c-format
 msgid ""
 "%d variable(s) cannot be recoded into %d variable(s).  Specify the same "
 "number of variables as input and output variables."
 msgstr ""
 
-#: src/recode.c:296
+#: src/recode.c:297
 #, c-format
 msgid ""
 "There is no string variable named %s.  (All string variables specified on "
 #, c-format
 msgid ""
 "There is no string variable named %s.  (All string variables specified on "
@@ -3179,83 +3094,83 @@ msgid ""
 "variable.)"
 msgstr ""
 
 "variable.)"
 msgstr ""
 
-#: src/recode.c:305
+#: src/recode.c:306
 #, c-format
 msgid ""
 "Type mismatch between input and output variables.  Output variable %s is not "
 "a string variable, but all the input variables are string variables."
 msgstr ""
 
 #, c-format
 msgid ""
 "Type mismatch between input and output variables.  Output variable %s is not "
 "a string variable, but all the input variables are string variables."
 msgstr ""
 
-#: src/recode.c:324
+#: src/recode.c:325
 #, c-format
 msgid "Type mismatch after INTO: %s is not a numeric variable."
 msgstr ""
 
 #, c-format
 msgid "Type mismatch after INTO: %s is not a numeric variable."
 msgstr ""
 
-#: src/recode.c:354
+#: src/recode.c:355
 msgid ""
 "INTO must be used when the input values are numeric and output values are "
 "string."
 msgstr ""
 
 msgid ""
 "INTO must be used when the input values are numeric and output values are "
 "string."
 msgstr ""
 
-#: src/recode.c:362
+#: src/recode.c:363
 msgid ""
 "INTO must be used when the input values are string and output values are "
 "numeric."
 msgstr ""
 
 msgid ""
 "INTO must be used when the input values are string and output values are "
 "numeric."
 msgstr ""
 
-#: src/recode.c:485
+#: src/recode.c:486
 msgid "expecting output value"
 msgstr ""
 
 msgid "expecting output value"
 msgstr ""
 
-#: src/recode.c:499
+#: src/recode.c:500
 msgid ""
 "Inconsistent output types.  The output values must be all numeric or all "
 "string."
 msgstr ""
 
 msgid ""
 "Inconsistent output types.  The output values must be all numeric or all "
 "string."
 msgstr ""
 
-#: src/recode.c:550
+#: src/recode.c:551
 msgid "following LO THRU"
 msgstr ""
 
 msgid "following LO THRU"
 msgstr ""
 
-#: src/recode.c:566 src/recode.c:595
+#: src/recode.c:567 src/recode.c:596
 msgid "in source value"
 msgstr ""
 
 msgid "in source value"
 msgstr ""
 
-#: src/recode.c:608
+#: src/recode.c:609
 msgid ""
 "Keyword CONVERT may only be used with string input values and numeric output "
 "values."
 msgstr ""
 
 msgid ""
 "Keyword CONVERT may only be used with string input values and numeric output "
 "values."
 msgstr ""
 
-#: src/rename-vars.c:47
+#: src/rename-vars.c:48
 msgid ""
 "RENAME VARS may not be used after TEMPORARY.  Temporary transformations will "
 "be made permanent."
 msgstr ""
 
 msgid ""
 "RENAME VARS may not be used after TEMPORARY.  Temporary transformations will "
 "be made permanent."
 msgstr ""
 
-#: src/rename-vars.c:59
+#: src/rename-vars.c:60
 msgid "`(' expected."
 msgstr ""
 
 msgid "`(' expected."
 msgstr ""
 
-#: src/rename-vars.c:67
+#: src/rename-vars.c:68
 msgid "`=' expected between lists of new and old variable names."
 msgstr ""
 
 msgid "`=' expected between lists of new and old variable names."
 msgstr ""
 
-#: src/rename-vars.c:85
+#: src/rename-vars.c:86
 msgid "`)' expected after variable names."
 msgstr ""
 
 msgid "`)' expected after variable names."
 msgstr ""
 
-#: src/rename-vars.c:95
+#: src/rename-vars.c:96
 #, c-format
 msgid "Renaming would duplicate variable name %s."
 msgstr ""
 
 #, c-format
 msgid "Renaming would duplicate variable name %s."
 msgstr ""
 
-#: src/repeat.c:150
+#: src/repeat.c:151
 #, c-format
 msgid "Identifier %s is given twice."
 msgstr ""
 
 #, c-format
 msgid "Identifier %s is given twice."
 msgstr ""
 
-#: src/repeat.c:193
+#: src/repeat.c:194
 #, c-format
 msgid ""
 "There must be the same number of substitutions for each dummy variable "
 #, c-format
 msgid ""
 "There must be the same number of substitutions for each dummy variable "
@@ -3263,11 +3178,11 @@ msgid ""
 "s as well, but %d were specified."
 msgstr ""
 
 "s as well, but %d were specified."
 msgstr ""
 
-#: src/repeat.c:298
+#: src/repeat.c:299
 msgid "No commands in scope."
 msgstr ""
 
 msgid "No commands in scope."
 msgstr ""
 
-#: src/repeat.c:486
+#: src/repeat.c:487
 msgid "No matching DO REPEAT."
 msgstr ""
 
 msgid "No matching DO REPEAT."
 msgstr ""
 
@@ -3280,129 +3195,111 @@ msgstr ""
 msgid "Cannot sample %d observations from a population of %d."
 msgstr ""
 
 msgid "Cannot sample %d observations from a population of %d."
 msgstr ""
 
-#: src/sel-if.c:99
+#: src/sel-if.c:100
 msgid "The filter variable must be numeric."
 msgstr ""
 
 msgid "The filter variable must be numeric."
 msgstr ""
 
-#: src/sel-if.c:105
+#: src/sel-if.c:106
 msgid "The filter variable may not be scratch."
 msgstr ""
 
 msgid "The filter variable may not be scratch."
 msgstr ""
 
-#: src/sel-if.c:136
+#: src/sel-if.c:137
 msgid "Only last instance of this command is in effect."
 msgstr ""
 
 msgid "Only last instance of this command is in effect."
 msgstr ""
 
-#: src/sfm-read.c:147
+#: src/sfm-read.c:140
 msgid "corrupt system file: "
 msgstr ""
 
 msgid "corrupt system file: "
 msgstr ""
 
-#: src/sfm-read.c:163 src/sfm-write.c:743
+#: src/sfm-read.c:157 src/sfm-write.c:808
 #, c-format
 msgid "%s: Closing system file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Closing system file: %s."
 msgstr ""
 
-#: src/sfm-read.c:237
-#, c-format
-msgid "Cannot read file %s as system file: already opened for %s."
-msgstr ""
-
-#: src/sfm-read.c:242
-#, c-format
-msgid "%s: Opening system-file handle %s for reading."
-msgstr ""
-
-#: src/sfm-read.c:250
+#: src/sfm-read.c:240
 #, c-format
 msgid ""
 "An error occurred while opening \"%s\" for reading as a system file: %s."
 msgstr ""
 
 #, c-format
 msgid ""
 "An error occurred while opening \"%s\" for reading as a system file: %s."
 msgstr ""
 
-#: src/sfm-read.c:285
+#: src/sfm-read.c:258
 #, c-format
 msgid ""
 "%s: Weighting variable may not be a continuation of a long string variable."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s: Weighting variable may not be a continuation of a long string variable."
 msgstr ""
 
-#: src/sfm-read.c:288
+#: src/sfm-read.c:261
 #, c-format
 msgid "%s: Weighting variable may not be a string variable."
 msgstr ""
 
 #, c-format
 msgid "%s: Weighting variable may not be a string variable."
 msgstr ""
 
-#: src/sfm-read.c:313
+#: src/sfm-read.c:286
 #, c-format
 msgid ""
 "%s: Orphaned variable index record (type 4).  Type 4 records must always "
 "immediately follow type 3 records."
 msgstr ""
 
 #, 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:362
+#: src/sfm-read.c:335
 #, c-format
 msgid "%s: Unrecognized record type 7, subtype %d encountered in system file."
 msgstr ""
 
 #, c-format
 msgid "%s: Unrecognized record type 7, subtype %d encountered in system file."
 msgstr ""
 
-#: src/sfm-read.c:387
+#: src/sfm-read.c:360
 #, c-format
 msgid "%s: Unrecognized record type %d."
 msgstr ""
 
 #, c-format
 msgid "%s: Unrecognized record type %d."
 msgstr ""
 
-#: src/sfm-read.c:394
-msgid "Read system-file dictionary successfully."
-msgstr ""
-
-#: src/sfm-read.c:401
-msgid "Error reading system-file header."
-msgstr ""
-
-#: src/sfm-read.c:425
+#: src/sfm-read.c:392
 #, c-format
 msgid ""
 "%s: Bad size (%d) or count (%d) field on record type 7, subtype 3.\tExpected "
 "size %d, count 8."
 msgstr ""
 
 #, 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:437
+#: src/sfm-read.c:403
 #, c-format
 msgid ""
 "%s: Floating-point representation in system file is not IEEE-754.  PSPP "
 "cannot convert between floating-point formats."
 msgstr ""
 
 #, 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:453
+#: src/sfm-read.c:419
 #, c-format
 msgid ""
 "%s: File-indicated endianness (%s) does not match endianness intuited from "
 "file header (%s)."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s: File-indicated endianness (%s) does not match endianness intuited from "
 "file header (%s)."
 msgstr ""
 
-#: src/sfm-read.c:456 src/sfm-read.c:457
+#: src/sfm-read.c:422 src/sfm-read.c:423
 msgid "big-endian"
 msgstr ""
 
 msgid "big-endian"
 msgstr ""
 
-#: src/sfm-read.c:456 src/sfm-read.c:457
+#: src/sfm-read.c:422 src/sfm-read.c:423
 msgid "little-endian"
 msgstr ""
 
 msgid "little-endian"
 msgstr ""
 
-#: src/sfm-read.c:458
+#: src/sfm-read.c:424
 msgid "unknown"
 msgstr ""
 
 msgid "unknown"
 msgstr ""
 
-#: src/sfm-read.c:462
+#: src/sfm-read.c:428
 #, c-format
 msgid "%s: File-indicated character representation code (%s) is not ASCII."
 msgstr ""
 
 #, c-format
 msgid "%s: File-indicated character representation code (%s) is not ASCII."
 msgstr ""
 
-#: src/sfm-read.c:466
+#: src/sfm-read.c:432
 msgid "DEC Kanji"
 msgstr ""
 
 msgid "DEC Kanji"
 msgstr ""
 
-#: src/sfm-read.c:485
+#: src/sfm-read.c:448
 #, c-format
 msgid ""
 "%s: Bad size (%d) or count (%d) field on record type 7, subtype 4.\tExpected "
 "size %d, count 8."
 msgstr ""
 
 #, 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:500
+#: src/sfm-read.c:463
 #, c-format
 msgid ""
 "%s: File-indicated value is different from internal value for at least one "
 #, c-format
 msgid ""
 "%s: File-indicated value is different from internal value for at least one "
@@ -3410,263 +3307,236 @@ msgid ""
 "%g; LOWEST: %g, %g."
 msgstr ""
 
 "%g; LOWEST: %g, %g."
 msgstr ""
 
-#: src/sfm-read.c:531
+#: src/sfm-read.c:490
 #, c-format
 msgid ""
 "%s: Bad magic.  Proper system files begin with the four characters `$FL2'. "
 "This file will not be read."
 msgstr ""
 
 #, 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:574
+#: src/sfm-read.c:532
 #, c-format
 msgid ""
 "%s: File layout code has unexpected value %d.  Value should be 2, in big-"
 "endian or little-endian format."
 msgstr ""
 
 #, 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:590
+#: src/sfm-read.c:548
 #, c-format
 msgid "%s: Number of elements per case (%d) is not between 1 and %d."
 msgstr ""
 
 #, c-format
 msgid "%s: Number of elements per case (%d) is not between 1 and %d."
 msgstr ""
 
-#: src/sfm-read.c:599
+#: src/sfm-read.c:557
 #, c-format
 msgid ""
 "%s: Index of weighting variable (%d) is not between 0 and number of elements "
 "per case (%d)."
 msgstr ""
 
 #, 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:605
+#: src/sfm-read.c:564
 #, c-format
 msgid "%s: Number of cases in file (%ld) is not between -1 and %d."
 msgstr ""
 
 #, c-format
 msgid "%s: Number of cases in file (%ld) is not between -1 and %d."
 msgstr ""
 
-#: src/sfm-read.c:610
+#: src/sfm-read.c:569
 #, c-format
 msgid "%s: Compression bias (%g) is not the usual value of 100."
 msgstr ""
 
 #, c-format
 msgid "%s: Compression bias (%g) is not the usual value of 100."
 msgstr ""
 
-#: src/sfm-read.c:704
+#: src/sfm-read.c:662
 #, c-format
 msgid "%s: position %d: Bad record type (%d); the expected value was 2."
 msgstr ""
 
 #, c-format
 msgid "%s: position %d: Bad record type (%d); the expected value was 2."
 msgstr ""
 
-#: src/sfm-read.c:714
+#: src/sfm-read.c:672
 #, c-format
 msgid ""
 "%s: position %d: String variable does not have proper number of continuation "
 "records."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s: position %d: String variable does not have proper number of continuation "
 "records."
 msgstr ""
 
-#: src/sfm-read.c:723
+#: src/sfm-read.c:681
 #, c-format
 msgid "%s: position %d: Superfluous long string continuation record."
 msgstr ""
 
 #, c-format
 msgid "%s: position %d: Superfluous long string continuation record."
 msgstr ""
 
-#: src/sfm-read.c:729
+#: src/sfm-read.c:687
 #, c-format
 msgid "%s: position %d: Bad variable type code %d."
 msgstr ""
 
 #, c-format
 msgid "%s: position %d: Bad variable type code %d."
 msgstr ""
 
-#: src/sfm-read.c:732
+#: src/sfm-read.c:690
 #, c-format
 msgid "%s: position %d: Variable label indicator field is not 0 or 1."
 msgstr ""
 
 #, c-format
 msgid "%s: position %d: Variable label indicator field is not 0 or 1."
 msgstr ""
 
-#: src/sfm-read.c:736
+#: src/sfm-read.c:694
 #, c-format
 msgid ""
 "%s: position %d: Missing value indicator field is not -3, -2, 0, 1, 2, or 3."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s: position %d: Missing value indicator field is not -3, -2, 0, 1, 2, or 3."
 msgstr ""
 
-#: src/sfm-read.c:742
+#: src/sfm-read.c:700
 #, c-format
 msgid "%s: position %d: Variable name begins with invalid character."
 msgstr ""
 
 #, c-format
 msgid "%s: position %d: Variable name begins with invalid character."
 msgstr ""
 
-#: src/sfm-read.c:746
+#: src/sfm-read.c:704
 #, c-format
 msgid "%s: position %d: Variable name begins with lowercase letter %c."
 msgstr ""
 
 #, c-format
 msgid "%s: position %d: Variable name begins with lowercase letter %c."
 msgstr ""
 
-#: src/sfm-read.c:750
+#: src/sfm-read.c:708
 #, c-format
 msgid ""
 "%s: position %d: Variable name begins with octothorpe (`#').  Scratch "
 "variables should not appear in system files."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s: position %d: Variable name begins with octothorpe (`#').  Scratch "
 "variables should not appear in system files."
 msgstr ""
 
-#: src/sfm-read.c:765
+#: src/sfm-read.c:723
 #, c-format
 msgid "%s: position %d: Variable name character %d is lowercase letter %c."
 msgstr ""
 
 #, c-format
 msgid "%s: position %d: Variable name character %d is lowercase letter %c."
 msgstr ""
 
-#: src/sfm-read.c:774
+#: src/sfm-read.c:732
 #, c-format
 msgid ""
 "%s: position %d: character `\\%03o' (%c) is not valid in a variable name."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s: position %d: character `\\%03o' (%c) is not valid in a variable name."
 msgstr ""
 
-#: src/sfm-read.c:783
+#: src/sfm-read.c:741
 #, c-format
 msgid "%s: Duplicate variable name `%s' within system file."
 msgstr ""
 
 #, c-format
 msgid "%s: Duplicate variable name `%s' within system file."
 msgstr ""
 
-#: src/sfm-read.c:808
+#: src/sfm-read.c:762
 #, c-format
 msgid "%s: Variable %s indicates variable label of invalid length %d."
 msgstr ""
 
 #, c-format
 msgid "%s: Variable %s indicates variable label of invalid length %d."
 msgstr ""
 
-#: src/sfm-read.c:825
+#: src/sfm-read.c:779
 #, c-format
 msgid "%s: Long string variable %s may not have missing values."
 msgstr ""
 
 #, c-format
 msgid "%s: Long string variable %s may not have missing values."
 msgstr ""
 
-#: src/sfm-read.c:850
+#: src/sfm-read.c:804
 #, c-format
 msgid ""
 "%s: String variable %s may not have missing values specified as a range."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s: String variable %s may not have missing values specified as a range."
 msgstr ""
 
-#: src/sfm-read.c:888
+#: src/sfm-read.c:852
 #, c-format
 msgid "%s: Long string continuation records omitted at end of dictionary."
 msgstr ""
 
 #, c-format
 msgid "%s: Long string continuation records omitted at end of dictionary."
 msgstr ""
 
-#: src/sfm-read.c:892
+#: src/sfm-read.c:856
 #, c-format
 msgid ""
 "%s: System file header indicates %d variable positions but %d were read from "
 "file."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s: System file header indicates %d variable positions but %d were read from "
 "file."
 msgstr ""
 
-#: src/sfm-read.c:923
+#: src/sfm-read.c:884
 #, c-format
 msgid "%s: %s variable %s has %s format specifier %s."
 msgstr ""
 
 #, c-format
 msgid "%s: %s variable %s has %s format specifier %s."
 msgstr ""
 
-#: src/sfm-read.c:1003
+#: src/sfm-read.c:963
 #, c-format
 msgid ""
 "%s: Variable index record (type 4) does not immediately follow value label "
 "record (type 3) as it should."
 msgstr ""
 
 #, 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:1014
+#: src/sfm-read.c:974
 #, c-format
 msgid ""
 "%s: Number of variables associated with a value label (%d) is not between 1 "
 "and the number of variables (%d)."
 msgstr ""
 
 #, 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:1030
+#: src/sfm-read.c:990
 #, c-format
 msgid ""
 "%s: Variable index associated with value label (%d) is not between 1 and the "
 "number of values (%d)."
 msgstr ""
 
 #, 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:1037
+#: src/sfm-read.c:997
 #, 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 ""
 
 #, 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:1042
+#: src/sfm-read.c:1002
 #, c-format
 msgid "%s: Value labels are not allowed on long string variables (%s)."
 msgstr ""
 
 #, c-format
 msgid "%s: Value labels are not allowed on long string variables (%s)."
 msgstr ""
 
-#: src/sfm-read.c:1053
+#: src/sfm-read.c:1013
 #, 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 ""
 
 #, 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:1094
+#: src/sfm-read.c:1054
 #, c-format
 msgid "%s: File contains duplicate label for value %g for variable %s."
 msgstr ""
 
 #, c-format
 msgid "%s: File contains duplicate label for value %g for variable %s."
 msgstr ""
 
-#: src/sfm-read.c:1098
+#: src/sfm-read.c:1058
 #, c-format
 msgid "%s: File contains duplicate label for value `%.*s' for variable %s."
 msgstr ""
 
 #, c-format
 msgid "%s: File contains duplicate label for value `%.*s' for variable %s."
 msgstr ""
 
-#: src/sfm-read.c:1135 src/sfm-read.c:1338
+#: src/sfm-read.c:1093 src/sfm-read.c:1357
 #, c-format
 msgid "%s: Reading system file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Reading system file: %s."
 msgstr ""
 
-#: src/sfm-read.c:1138 src/sfm-read.c:1245 src/sfm-read.c:1287
+#: src/sfm-read.c:1096 src/sfm-read.c:1198 src/sfm-read.c:1240
 #, c-format
 msgid "%s: Unexpected end of file."
 msgstr ""
 
 #, c-format
 msgid "%s: Unexpected end of file."
 msgstr ""
 
-#: src/sfm-read.c:1157
+#: src/sfm-read.c:1113
 #, c-format
 msgid "%s: System file contains multiple type 6 (document) records."
 msgstr ""
 
 #, c-format
 msgid "%s: System file contains multiple type 6 (document) records."
 msgstr ""
 
-#: src/sfm-read.c:1163
+#: src/sfm-read.c:1119
 #, c-format
 msgid "%s: Number of document lines (%ld) must be greater than 0."
 msgstr ""
 
 #, c-format
 msgid "%s: Number of document lines (%ld) must be greater than 0."
 msgstr ""
 
-#: src/sfm-read.c:1196
+#: src/sfm-read.c:1151
 #, c-format
 msgid "%s: Error reading file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Error reading file: %s."
 msgstr ""
 
-#: src/sfm-read.c:1235
+#: src/sfm-read.c:1188
 #, c-format
 msgid "%s: Compressed data is corrupted.  Data ends in partial case."
 msgstr ""
 
 #, c-format
 msgid "%s: Compressed data is corrupted.  Data ends in partial case."
 msgstr ""
 
-#: src/sfm-read.c:1341
+#: src/sfm-read.c:1360
 #, c-format
 msgid "%s: Partial record at end of system file."
 msgstr ""
 
 #, c-format
 msgid "%s: Partial record at end of system file."
 msgstr ""
 
-#: src/sfm-read.c:1380
-msgid "reading as a system file"
-msgstr ""
-
-#: src/sfm-write.c:95
-#, c-format
-msgid "Cannot write file %s as system file: already opened for %s."
-msgstr ""
-
-#: src/sfm-write.c:101
-#, c-format
-msgid "%s: Opening system-file handle %s for writing."
-msgstr ""
-
-#: src/sfm-write.c:111
+#: src/sfm-write.c:143
 #, c-format
 #, c-format
-msgid ""
-"An error occurred while opening \"%s\" for writing as a system file: %s."
-msgstr ""
-
-#: src/sfm-write.c:165
-msgid "Wrote system-file header successfully."
+msgid "Error opening \"%s\" for writing as a system file: %s."
 msgstr ""
 
 msgstr ""
 
-#: src/sfm-write.c:170
-msgid "Error writing system-file header."
-msgstr ""
-
-#: src/sfm-write.c:606
+#: src/sfm-write.c:628
 #, c-format
 msgid "%s: Writing system file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Writing system file: %s."
 msgstr ""
 
-#: src/sfm-write.c:754
-msgid "writing as a system file"
-msgstr ""
-
 #: src/sort.c:197
 msgid "`A' or `D' expected inside parentheses."
 msgstr ""
 #: src/sort.c:197
 msgid "`A' or `D' expected inside parentheses."
 msgstr ""
@@ -3682,141 +3552,141 @@ msgid ""
 "each.  (PSPP workspace is currently restricted to a maximum of %d KB.)"
 msgstr ""
 
 "each.  (PSPP workspace is currently restricted to a maximum of %d KB.)"
 msgstr ""
 
-#: src/sysfile-info.c:94
+#: src/sysfile-info.c:96
 msgid "File:"
 msgstr ""
 
 msgid "File:"
 msgstr ""
 
-#: src/sysfile-info.c:96
+#: src/sysfile-info.c:98
 msgid "Label:"
 msgstr ""
 
 msgid "Label:"
 msgstr ""
 
-#: src/sysfile-info.c:100
+#: src/sysfile-info.c:102
 msgid "No label."
 msgstr ""
 
 msgid "No label."
 msgstr ""
 
-#: src/sysfile-info.c:103
+#: src/sysfile-info.c:105
 msgid "Created:"
 msgstr ""
 
 msgid "Created:"
 msgstr ""
 
-#: src/sysfile-info.c:106
+#: src/sysfile-info.c:108
 msgid "Endian:"
 msgstr ""
 
 msgid "Endian:"
 msgstr ""
 
-#: src/sysfile-info.c:107
+#: src/sysfile-info.c:109
 msgid "Big."
 msgstr ""
 
 msgid "Big."
 msgstr ""
 
-#: src/sysfile-info.c:107
+#: src/sysfile-info.c:109
 msgid "Little."
 msgstr ""
 
 msgid "Little."
 msgstr ""
 
-#: src/sysfile-info.c:108
+#: src/sysfile-info.c:110
 msgid "Variables:"
 msgstr ""
 
 msgid "Variables:"
 msgstr ""
 
-#: src/sysfile-info.c:111
+#: src/sysfile-info.c:113
 msgid "Cases:"
 msgstr ""
 
 msgid "Cases:"
 msgstr ""
 
-#: src/sysfile-info.c:114
+#: src/sysfile-info.c:116
 msgid "Type:"
 msgstr ""
 
 msgid "Type:"
 msgstr ""
 
-#: src/sysfile-info.c:115
+#: src/sysfile-info.c:117
 msgid "System File."
 msgstr ""
 
 msgid "System File."
 msgstr ""
 
-#: src/sysfile-info.c:116
+#: src/sysfile-info.c:118
 msgid "Weight:"
 msgstr ""
 
 msgid "Weight:"
 msgstr ""
 
-#: src/sysfile-info.c:120
+#: src/sysfile-info.c:122
 msgid "Not weighted."
 msgstr ""
 
 msgid "Not weighted."
 msgstr ""
 
-#: src/sysfile-info.c:122
+#: src/sysfile-info.c:124
 msgid "Mode:"
 msgstr ""
 
 msgid "Mode:"
 msgstr ""
 
-#: src/sysfile-info.c:124
+#: src/sysfile-info.c:126
 #, c-format
 msgid "Compression %s."
 msgstr ""
 
 #, c-format
 msgid "Compression %s."
 msgstr ""
 
-#: src/sysfile-info.c:124
+#: src/sysfile-info.c:126
 msgid "on"
 msgstr ""
 
 msgid "on"
 msgstr ""
 
-#: src/sysfile-info.c:124
+#: src/sysfile-info.c:126
 msgid "off"
 msgstr ""
 
 msgid "off"
 msgstr ""
 
-#: src/sysfile-info.c:133 src/sysfile-info.c:370
+#: src/sysfile-info.c:135 src/sysfile-info.c:372
 msgid "Description"
 msgstr ""
 
 msgid "Description"
 msgstr ""
 
-#: src/sysfile-info.c:134 src/sysfile-info.c:368
+#: src/sysfile-info.c:136 src/sysfile-info.c:370
 msgid "Position"
 msgstr ""
 
 msgid "Position"
 msgstr ""
 
-#: src/sysfile-info.c:191
+#: src/sysfile-info.c:193
 msgid "The active file does not have a file label."
 msgstr ""
 
 msgid "The active file does not have a file label."
 msgstr ""
 
-#: src/sysfile-info.c:194
+#: src/sysfile-info.c:196
 msgid "File label:"
 msgstr ""
 
 msgid "File label:"
 msgstr ""
 
-#: src/sysfile-info.c:256
+#: src/sysfile-info.c:258
 msgid "No variables to display."
 msgstr ""
 
 msgid "No variables to display."
 msgstr ""
 
-#: src/sysfile-info.c:275
+#: src/sysfile-info.c:277
 msgid "Macros not supported."
 msgstr ""
 
 msgid "Macros not supported."
 msgstr ""
 
-#: src/sysfile-info.c:285
+#: src/sysfile-info.c:287
 msgid "The active file dictionary does not contain any documents."
 msgstr ""
 
 msgid "The active file dictionary does not contain any documents."
 msgstr ""
 
-#: src/sysfile-info.c:294
+#: src/sysfile-info.c:296
 msgid "Documents in the active file:"
 msgstr ""
 
 msgid "Documents in the active file:"
 msgstr ""
 
-#: src/sysfile-info.c:372 src/sysfile-info.c:530 src/vfm.c:876
+#: src/sysfile-info.c:374 src/sysfile-info.c:532 src/vfm.c:877
 msgid "Label"
 msgstr ""
 
 msgid "Label"
 msgstr ""
 
-#: src/sysfile-info.c:444
+#: src/sysfile-info.c:446
 #, c-format
 msgid "Format: %s"
 msgstr ""
 
 #, c-format
 msgid "Format: %s"
 msgstr ""
 
-#: src/sysfile-info.c:451
+#: src/sysfile-info.c:453
 #, c-format
 msgid "Print Format: %s"
 msgstr ""
 
 #, c-format
 msgid "Print Format: %s"
 msgstr ""
 
-#: src/sysfile-info.c:454
+#: src/sysfile-info.c:456
 #, c-format
 msgid "Write Format: %s"
 msgstr ""
 
 #, c-format
 msgid "Write Format: %s"
 msgstr ""
 
-#: src/sysfile-info.c:462
+#: src/sysfile-info.c:464
 msgid "Missing Values: "
 msgstr ""
 
 msgid "Missing Values: "
 msgstr ""
 
-#: src/sysfile-info.c:529 src/vfm.c:875 src/crosstabs.q:1068
-#: src/crosstabs.q:1095 src/crosstabs.q:1115 src/crosstabs.q:1137
-#: src/examine.q:1125 src/frequencies.q:1056 src/frequencies.q:1174
+#: src/sysfile-info.c:531 src/vfm.c:876 src/crosstabs.q:1099
+#: src/crosstabs.q:1126 src/crosstabs.q:1146 src/crosstabs.q:1168
+#: src/examine.q:1179 src/frequencies.q:1083 src/frequencies.q:1204
 msgid "Value"
 msgstr ""
 
 msgid "Value"
 msgstr ""
 
-#: src/sysfile-info.c:586
+#: src/sysfile-info.c:588
 msgid "No vectors defined."
 msgstr ""
 
 msgid "No vectors defined."
 msgstr ""
 
-#: src/sysfile-info.c:601
+#: src/sysfile-info.c:603
 msgid "Vector"
 msgstr ""
 
 msgid "Vector"
 msgstr ""
 
@@ -3831,36 +3701,36 @@ msgid ""
 "bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
 msgstr ""
 
 "bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
 msgstr ""
 
-#: src/temporary.c:45
+#: src/temporary.c:46
 msgid "This command is not valid inside DO IF or LOOP."
 msgstr ""
 
 msgid "This command is not valid inside DO IF or LOOP."
 msgstr ""
 
-#: src/temporary.c:52
+#: src/temporary.c:53
 msgid ""
 "This command may only appear once between procedures and procedure-like "
 "commands."
 msgstr ""
 
 msgid ""
 "This command may only appear once between procedures and procedure-like "
 "commands."
 msgstr ""
 
-#: src/title.c:55
+#: src/title.c:56
 #, c-format
 msgid "%s before: %s\n"
 msgstr ""
 
 #, c-format
 msgid "%s before: %s\n"
 msgstr ""
 
-#: src/title.c:55
+#: src/title.c:56
 msgid "<none>"
 msgstr ""
 
 msgid "<none>"
 msgstr ""
 
-#: src/title.c:67
+#: src/title.c:68
 #, c-format
 msgid "%s: `.' expected after string."
 msgstr ""
 
 #, c-format
 msgid "%s: `.' expected after string."
 msgstr ""
 
-#: src/title.c:83
+#: src/title.c:84
 #, c-format
 msgid "%s after: %s\n"
 msgstr ""
 
 #, c-format
 msgid "%s after: %s\n"
 msgstr ""
 
-#: src/title.c:139
+#: src/title.c:140
 #, c-format
 msgid "Document entered %s %02d:%02d:%02d by %s (%s):"
 msgstr ""
 #, c-format
 msgid "Document entered %s %02d:%02d:%02d by %s (%s):"
 msgstr ""
@@ -3889,29 +3759,29 @@ msgstr ""
 msgid "Truncating variable label to 255 characters."
 msgstr ""
 
 msgid "Truncating variable label to 255 characters."
 msgstr ""
 
-#: src/vars-prs.c:48
+#: src/vars-prs.c:49
 #, c-format
 msgid "%s is not a variable name."
 msgstr ""
 
 #, c-format
 msgid "%s is not a variable name."
 msgstr ""
 
-#: src/vars-prs.c:100
+#: src/vars-prs.c:101
 msgid "ordinary"
 msgstr ""
 
 msgid "ordinary"
 msgstr ""
 
-#: src/vars-prs.c:102
+#: src/vars-prs.c:103
 msgid "system"
 msgstr ""
 
 msgid "system"
 msgstr ""
 
-#: src/vars-prs.c:104
+#: src/vars-prs.c:105
 msgid "scratch"
 msgstr ""
 
 msgid "scratch"
 msgstr ""
 
-#: src/vars-prs.c:209
+#: src/vars-prs.c:210
 #, c-format
 msgid "%s TO %s is not valid syntax since %s precedes %s in the dictionary."
 msgstr ""
 
 #, c-format
 msgid "%s TO %s is not valid syntax since %s precedes %s in the dictionary."
 msgstr ""
 
-#: src/vars-prs.c:219
+#: src/vars-prs.c:220
 #, c-format
 msgid ""
 "When using the TO keyword to specify several variables, both variables must "
 #, c-format
 msgid ""
 "When using the TO keyword to specify several variables, both variables must "
@@ -3919,700 +3789,706 @@ msgid ""
 "system variables.  %s is a %s variable, whereas %s is %s."
 msgstr ""
 
 "system variables.  %s is a %s variable, whereas %s is %s."
 msgstr ""
 
-#: src/vars-prs.c:237
+#: src/vars-prs.c:238
 #, c-format
 msgid "Scratch variables (such as %s) are not allowed here."
 msgstr ""
 
 #, c-format
 msgid "Scratch variables (such as %s) are not allowed here."
 msgstr ""
 
-#: src/vars-prs.c:260
+#: src/vars-prs.c:261
 #, c-format
 msgid ""
 "%s is not a numeric variable.  It will not be included in the variable list."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s is not a numeric variable.  It will not be included in the variable list."
 msgstr ""
 
-#: src/vars-prs.c:263
+#: src/vars-prs.c:264
 #, c-format
 msgid ""
 "%s is not a string variable.  It will not be included in the variable list."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s is not a string variable.  It will not be included in the variable list."
 msgstr ""
 
-#: src/vars-prs.c:267
+#: src/vars-prs.c:268
 #, c-format
 msgid ""
 "%s and %s are not the same type.  All variables in this variable list must "
 "be of the same type.  %s will be omitted from list."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s and %s are not the same type.  All variables in this variable list must "
 "be of the same type.  %s will be omitted from list."
 msgstr ""
 
-#: src/vars-prs.c:272
+#: src/vars-prs.c:273
 #, c-format
 msgid "Variable %s appears twice in variable list."
 msgstr ""
 
 #, c-format
 msgid "Variable %s appears twice in variable list."
 msgstr ""
 
-#: src/vars-prs.c:352
+#: src/vars-prs.c:353
 msgid "incorrect use of TO convention"
 msgstr ""
 
 msgid "incorrect use of TO convention"
 msgstr ""
 
-#: src/vars-prs.c:399
+#: src/vars-prs.c:400
 msgid "Scratch variables not allowed here."
 msgstr ""
 
 msgid "Scratch variables not allowed here."
 msgstr ""
 
-#: src/vars-prs.c:421
+#: src/vars-prs.c:422
 msgid "Prefixes don't match in use of TO convention."
 msgstr ""
 
 msgid "Prefixes don't match in use of TO convention."
 msgstr ""
 
-#: src/vars-prs.c:426
+#: src/vars-prs.c:427
 msgid "Bad bounds in use of TO convention."
 msgstr ""
 
 msgid "Bad bounds in use of TO convention."
 msgstr ""
 
-#: src/vector.c:66
+#: src/vector.c:67
 #, c-format
 msgid "Vector name %s is given twice."
 msgstr ""
 
 #, c-format
 msgid "Vector name %s is given twice."
 msgstr ""
 
-#: src/vector.c:72
+#: src/vector.c:73
 #, c-format
 msgid "There is already a vector with name %s."
 msgstr ""
 
 #, c-format
 msgid "There is already a vector with name %s."
 msgstr ""
 
-#: src/vector.c:93
+#: src/vector.c:94
 msgid ""
 "A slash must be used to separate each vector specification when using the "
 "long form.  Commands such as VECTOR A,B=Q1 TO Q20 are not supported."
 msgstr ""
 
 msgid ""
 "A slash must be used to separate each vector specification when using the "
 "long form.  Commands such as VECTOR A,B=Q1 TO Q20 are not supported."
 msgstr ""
 
-#: src/vector.c:127
+#: src/vector.c:128
 msgid "Vectors must have at least one element."
 msgstr ""
 
 msgid "Vectors must have at least one element."
 msgstr ""
 
-#: src/vector.c:141
+#: src/vector.c:142
 #, c-format
 msgid "%s%d is too long for a variable name."
 msgstr ""
 
 #, c-format
 msgid "%s%d is too long for a variable name."
 msgstr ""
 
-#: src/vector.c:179
+#: src/vector.c:180
 msgid ""
 "The syntax for this command does not match the expected syntax for either "
 "the long form or the short form of VECTOR."
 msgstr ""
 
 msgid ""
 "The syntax for this command does not match the expected syntax for either "
 "the long form or the short form of VECTOR."
 msgstr ""
 
-#: src/weight.c:52
+#: src/weight.c:53
 msgid "The weighting variable must be numeric."
 msgstr ""
 
 msgid "The weighting variable must be numeric."
 msgstr ""
 
-#: src/weight.c:57
+#: src/weight.c:58
 msgid "The weighting variable may not be scratch."
 msgstr ""
 
 msgid "The weighting variable may not be scratch."
 msgstr ""
 
-#: src/crosstabs.q:241
+#: src/crosstabs.q:261
 msgid ""
 "Missing mode REPORT not allowed in general mode.  Assuming MISSING=TABLE."
 msgstr ""
 
 msgid ""
 "Missing mode REPORT not allowed in general mode.  Assuming MISSING=TABLE."
 msgstr ""
 
-#: src/crosstabs.q:251
+#: src/crosstabs.q:271
 msgid "Write mode ALL not allowed in general mode.  Assuming WRITE=CELLS."
 msgstr ""
 
 msgid "Write mode ALL not allowed in general mode.  Assuming WRITE=CELLS."
 msgstr ""
 
-#: src/crosstabs.q:312
+#: src/crosstabs.q:332
 msgid "expecting BY"
 msgstr ""
 
 msgid "expecting BY"
 msgstr ""
 
-#: src/crosstabs.q:379
+#: src/crosstabs.q:399
 msgid "VARIABLES must be specified before TABLES."
 msgstr ""
 
 msgid "VARIABLES must be specified before TABLES."
 msgstr ""
 
-#: src/crosstabs.q:416
+#: src/crosstabs.q:436
 #, c-format
 msgid "Maximum value (%ld) less than minimum value (%ld)."
 msgstr ""
 
 #, c-format
 msgid "Maximum value (%ld) less than minimum value (%ld)."
 msgstr ""
 
-#: src/crosstabs.q:769
+#: src/crosstabs.q:800
 msgid "Summary."
 msgstr ""
 
 msgid "Summary."
 msgstr ""
 
-#: src/crosstabs.q:771 src/examine.q:784
+#: src/crosstabs.q:802 src/examine.q:837
 msgid "Cases"
 msgstr ""
 
 msgid "Cases"
 msgstr ""
 
-#: src/crosstabs.q:772 src/examine.q:722 src/frequencies.q:1054
-#: src/frequencies.q:1421
+#: src/crosstabs.q:803 src/examine.q:775 src/frequencies.q:1081
+#: src/frequencies.q:1454
 msgid "Valid"
 msgstr ""
 
 msgid "Valid"
 msgstr ""
 
-#: src/crosstabs.q:773 src/examine.q:723 src/frequencies.q:1121
-#: src/frequencies.q:1422
+#: src/crosstabs.q:804 src/examine.q:776 src/frequencies.q:1149
+#: src/frequencies.q:1455
 msgid "Missing"
 msgstr ""
 
 msgid "Missing"
 msgstr ""
 
-#: src/crosstabs.q:774 src/crosstabs.q:977 src/crosstabs.q:1690
-#: src/examine.q:724 src/frequencies.q:1130 src/oneway.q:306 src/oneway.q:483
+#: src/crosstabs.q:805 src/crosstabs.q:1008 src/crosstabs.q:1722
+#: src/examine.q:777 src/frequencies.q:1158 src/oneway.q:307 src/oneway.q:486
 msgid "Total"
 msgstr ""
 
 msgid "Total"
 msgstr ""
 
-#: src/crosstabs.q:784 src/examine.q:805 src/frequencies.q:1420
-#: src/oneway.q:393 src/t-test.q:680 src/t-test.q:703 src/t-test.q:828
+#: src/crosstabs.q:815 src/examine.q:858 src/frequencies.q:1453
+#: src/oneway.q:395 src/t-test.q:682 src/t-test.q:705 src/t-test.q:830
 #: src/t-test.q:1365
 msgid "N"
 msgstr ""
 
 #: src/t-test.q:1365
 msgid "N"
 msgstr ""
 
-#: src/crosstabs.q:785 src/examine.q:807 src/frequencies.q:1058
-#: src/frequencies.q:1059 src/frequencies.q:1060
+#: src/crosstabs.q:816 src/examine.q:860 src/frequencies.q:1085
+#: src/frequencies.q:1086 src/frequencies.q:1087
 msgid "Percent"
 msgstr ""
 
 msgid "Percent"
 msgstr ""
 
-#: src/crosstabs.q:1027
+#: src/crosstabs.q:1058
 msgid "count"
 msgstr ""
 
 msgid "count"
 msgstr ""
 
-#: src/crosstabs.q:1028
+#: src/crosstabs.q:1059
 msgid "row %"
 msgstr ""
 
 msgid "row %"
 msgstr ""
 
-#: src/crosstabs.q:1029
+#: src/crosstabs.q:1060
 msgid "column %"
 msgstr ""
 
 msgid "column %"
 msgstr ""
 
-#: src/crosstabs.q:1030
+#: src/crosstabs.q:1061
 msgid "total %"
 msgstr ""
 
 msgid "total %"
 msgstr ""
 
-#: src/crosstabs.q:1031
+#: src/crosstabs.q:1062
 msgid "expected"
 msgstr ""
 
 msgid "expected"
 msgstr ""
 
-#: src/crosstabs.q:1032
+#: src/crosstabs.q:1063
 msgid "residual"
 msgstr ""
 
 msgid "residual"
 msgstr ""
 
-#: src/crosstabs.q:1033
+#: src/crosstabs.q:1064
 msgid "std. resid."
 msgstr ""
 
 msgid "std. resid."
 msgstr ""
 
-#: src/crosstabs.q:1034
+#: src/crosstabs.q:1065
 msgid "adj. resid."
 msgstr ""
 
 msgid "adj. resid."
 msgstr ""
 
-#: src/crosstabs.q:1067 src/crosstabs.q:1094 src/crosstabs.q:1114
-#: src/crosstabs.q:1135 src/examine.q:448
+#: src/crosstabs.q:1098 src/crosstabs.q:1125 src/crosstabs.q:1145
+#: src/crosstabs.q:1166 src/examine.q:495
 msgid "Statistic"
 msgstr ""
 
 msgid "Statistic"
 msgstr ""
 
-#: src/crosstabs.q:1069 src/oneway.q:276 src/oneway.q:711 src/t-test.q:979
-#: src/t-test.q:1171 src/t-test.q:1263
+#: src/crosstabs.q:1100 src/oneway.q:278 src/oneway.q:707 src/t-test.q:980
+#: src/t-test.q:1172 src/t-test.q:1264
 msgid "df"
 msgstr ""
 
 msgid "df"
 msgstr ""
 
-#: src/crosstabs.q:1071
+#: src/crosstabs.q:1102
 msgid "Asymp. Sig. (2-sided)"
 msgstr ""
 
 msgid "Asymp. Sig. (2-sided)"
 msgstr ""
 
-#: src/crosstabs.q:1073
+#: src/crosstabs.q:1104
 msgid "Exact. Sig. (2-sided)"
 msgstr ""
 
 msgid "Exact. Sig. (2-sided)"
 msgstr ""
 
-#: src/crosstabs.q:1075
+#: src/crosstabs.q:1106
 msgid "Exact. Sig. (1-sided)"
 msgstr ""
 
 msgid "Exact. Sig. (1-sided)"
 msgstr ""
 
-#: src/crosstabs.q:1093 src/crosstabs.q:1134
+#: src/crosstabs.q:1124 src/crosstabs.q:1165
 msgid "Category"
 msgstr ""
 
 msgid "Category"
 msgstr ""
 
-#: src/crosstabs.q:1096 src/crosstabs.q:1138
+#: src/crosstabs.q:1127 src/crosstabs.q:1169
 msgid "Asymp. Std. Error"
 msgstr ""
 
 msgid "Asymp. Std. Error"
 msgstr ""
 
-#: src/crosstabs.q:1097 src/crosstabs.q:1139
+#: src/crosstabs.q:1128 src/crosstabs.q:1170
 msgid "Approx. T"
 msgstr ""
 
 msgid "Approx. T"
 msgstr ""
 
-#: src/crosstabs.q:1098 src/crosstabs.q:1140
+#: src/crosstabs.q:1129 src/crosstabs.q:1171
 msgid "Approx. Sig."
 msgstr ""
 
 msgid "Approx. Sig."
 msgstr ""
 
-#: src/crosstabs.q:1113
+#: src/crosstabs.q:1144
 #, c-format
 msgid " 95%% Confidence Interval"
 msgstr ""
 
 #, c-format
 msgid " 95%% Confidence Interval"
 msgstr ""
 
-#: src/crosstabs.q:1116 src/t-test.q:983 src/t-test.q:1168 src/t-test.q:1266
+#: src/crosstabs.q:1147 src/t-test.q:984 src/t-test.q:1169 src/t-test.q:1267
 msgid "Lower"
 msgstr ""
 
 msgid "Lower"
 msgstr ""
 
-#: src/crosstabs.q:1117 src/t-test.q:984 src/t-test.q:1169 src/t-test.q:1267
+#: src/crosstabs.q:1148 src/t-test.q:985 src/t-test.q:1170 src/t-test.q:1268
 msgid "Upper"
 msgstr ""
 
 msgid "Upper"
 msgstr ""
 
-#: src/crosstabs.q:1136
+#: src/crosstabs.q:1167
 msgid "Type"
 msgstr ""
 
 msgid "Type"
 msgstr ""
 
-#: src/crosstabs.q:1884
+#: src/crosstabs.q:1916
 msgid "Pearson Chi-Square"
 msgstr ""
 
 msgid "Pearson Chi-Square"
 msgstr ""
 
-#: src/crosstabs.q:1885
+#: src/crosstabs.q:1917
 msgid "Likelihood Ratio"
 msgstr ""
 
 msgid "Likelihood Ratio"
 msgstr ""
 
-#: src/crosstabs.q:1886
+#: src/crosstabs.q:1918
 msgid "Fisher's Exact Test"
 msgstr ""
 
 msgid "Fisher's Exact Test"
 msgstr ""
 
-#: src/crosstabs.q:1887
+#: src/crosstabs.q:1919
 msgid "Continuity Correction"
 msgstr ""
 
 msgid "Continuity Correction"
 msgstr ""
 
-#: src/crosstabs.q:1888
+#: src/crosstabs.q:1920
 msgid "Linear-by-Linear Association"
 msgstr ""
 
 msgid "Linear-by-Linear Association"
 msgstr ""
 
-#: src/crosstabs.q:1925 src/crosstabs.q:1995 src/crosstabs.q:2054
+#: src/crosstabs.q:1957 src/crosstabs.q:2027 src/crosstabs.q:2086
 msgid "N of Valid Cases"
 msgstr ""
 
 msgid "N of Valid Cases"
 msgstr ""
 
-#: src/crosstabs.q:1941 src/crosstabs.q:2070
+#: src/crosstabs.q:1973 src/crosstabs.q:2102
 msgid "Nominal by Nominal"
 msgstr ""
 
 msgid "Nominal by Nominal"
 msgstr ""
 
-#: src/crosstabs.q:1942 src/crosstabs.q:2071
+#: src/crosstabs.q:1974 src/crosstabs.q:2103
 msgid "Ordinal by Ordinal"
 msgstr ""
 
 msgid "Ordinal by Ordinal"
 msgstr ""
 
-#: src/crosstabs.q:1943
+#: src/crosstabs.q:1975
 msgid "Interval by Interval"
 msgstr ""
 
 msgid "Interval by Interval"
 msgstr ""
 
-#: src/crosstabs.q:1944
+#: src/crosstabs.q:1976
 msgid "Measure of Agreement"
 msgstr ""
 
 msgid "Measure of Agreement"
 msgstr ""
 
-#: src/crosstabs.q:1949
+#: src/crosstabs.q:1981
 msgid "Phi"
 msgstr ""
 
 msgid "Phi"
 msgstr ""
 
-#: src/crosstabs.q:1950
+#: src/crosstabs.q:1982
 msgid "Cramer's V"
 msgstr ""
 
 msgid "Cramer's V"
 msgstr ""
 
-#: src/crosstabs.q:1951
+#: src/crosstabs.q:1983
 msgid "Contingency Coefficient"
 msgstr ""
 
 msgid "Contingency Coefficient"
 msgstr ""
 
-#: src/crosstabs.q:1952
+#: src/crosstabs.q:1984
 msgid "Kendall's tau-b"
 msgstr ""
 
 msgid "Kendall's tau-b"
 msgstr ""
 
-#: src/crosstabs.q:1953
+#: src/crosstabs.q:1985
 msgid "Kendall's tau-c"
 msgstr ""
 
 msgid "Kendall's tau-c"
 msgstr ""
 
-#: src/crosstabs.q:1954
+#: src/crosstabs.q:1986
 msgid "Gamma"
 msgstr ""
 
 msgid "Gamma"
 msgstr ""
 
-#: src/crosstabs.q:1955
+#: src/crosstabs.q:1987
 msgid "Spearman Correlation"
 msgstr ""
 
 msgid "Spearman Correlation"
 msgstr ""
 
-#: src/crosstabs.q:1956
+#: src/crosstabs.q:1988
 msgid "Pearson's R"
 msgstr ""
 
 msgid "Pearson's R"
 msgstr ""
 
-#: src/crosstabs.q:1957
+#: src/crosstabs.q:1989
 msgid "Kappa"
 msgstr ""
 
 msgid "Kappa"
 msgstr ""
 
-#: src/crosstabs.q:2027
+#: src/crosstabs.q:2059
 #, c-format
 msgid "Odds Ratio for %s (%g / %g)"
 msgstr ""
 
 #, c-format
 msgid "Odds Ratio for %s (%g / %g)"
 msgstr ""
 
-#: src/crosstabs.q:2030
+#: src/crosstabs.q:2062
 #, c-format
 msgid "Odds Ratio for %s (%.*s / %.*s)"
 msgstr ""
 
 #, c-format
 msgid "Odds Ratio for %s (%.*s / %.*s)"
 msgstr ""
 
-#: src/crosstabs.q:2038
+#: src/crosstabs.q:2070
 #, c-format
 msgid "For cohort %s = %g"
 msgstr ""
 
 #, c-format
 msgid "For cohort %s = %g"
 msgstr ""
 
-#: src/crosstabs.q:2041
+#: src/crosstabs.q:2073
 #, c-format
 msgid "For cohort %s = %.*s"
 msgstr ""
 
 #, c-format
 msgid "For cohort %s = %.*s"
 msgstr ""
 
-#: src/crosstabs.q:2072
+#: src/crosstabs.q:2104
 msgid "Nominal by Interval"
 msgstr ""
 
 msgid "Nominal by Interval"
 msgstr ""
 
-#: src/crosstabs.q:2077
+#: src/crosstabs.q:2109
 msgid "Lambda"
 msgstr ""
 
 msgid "Lambda"
 msgstr ""
 
-#: src/crosstabs.q:2078
+#: src/crosstabs.q:2110
 msgid "Goodman and Kruskal tau"
 msgstr ""
 
 msgid "Goodman and Kruskal tau"
 msgstr ""
 
-#: src/crosstabs.q:2079
+#: src/crosstabs.q:2111
 msgid "Uncertainty Coefficient"
 msgstr ""
 
 msgid "Uncertainty Coefficient"
 msgstr ""
 
-#: src/crosstabs.q:2080
+#: src/crosstabs.q:2112
 msgid "Somers' d"
 msgstr ""
 
 msgid "Somers' d"
 msgstr ""
 
-#: src/crosstabs.q:2081
+#: src/crosstabs.q:2113
 msgid "Eta"
 msgstr ""
 
 msgid "Eta"
 msgstr ""
 
-#: src/crosstabs.q:2086
+#: src/crosstabs.q:2118
 msgid "Symmetric"
 msgstr ""
 
 msgid "Symmetric"
 msgstr ""
 
-#: src/crosstabs.q:2087 src/crosstabs.q:2088
+#: src/crosstabs.q:2119 src/crosstabs.q:2120
 #, c-format
 msgid "%s Dependent"
 msgstr ""
 
 #, c-format
 msgid "%s Dependent"
 msgstr ""
 
-#: src/examine.q:217 src/examine.q:229
+#: src/examine.q:264 src/examine.q:276
 #, c-format
 msgid "%s and %s are mutually exclusive"
 msgstr ""
 
 #, c-format
 msgid "%s and %s are mutually exclusive"
 msgstr ""
 
-#: src/examine.q:449 src/oneway.q:396 src/oneway.q:709
+#: src/examine.q:496 src/oneway.q:398 src/oneway.q:705
 msgid "Std. Error"
 msgstr ""
 
 msgid "Std. Error"
 msgstr ""
 
-#: src/examine.q:563 src/oneway.q:410
+#: src/examine.q:610 src/oneway.q:412
 msgid "Descriptives"
 msgstr ""
 
 msgid "Descriptives"
 msgstr ""
 
-#: src/examine.q:602 src/oneway.q:401
+#: src/examine.q:649 src/oneway.q:403
 #, c-format
 msgid "%g%% Confidence Interval for Mean"
 msgstr ""
 
 #, c-format
 msgid "%g%% Confidence Interval for Mean"
 msgstr ""
 
-#: src/examine.q:608 src/oneway.q:403
+#: src/examine.q:655 src/oneway.q:405
 msgid "Lower Bound"
 msgstr ""
 
 msgid "Lower Bound"
 msgstr ""
 
-#: src/examine.q:619 src/oneway.q:404
+#: src/examine.q:666 src/oneway.q:406
 msgid "Upper Bound"
 msgstr ""
 
 msgid "Upper Bound"
 msgstr ""
 
-#: src/examine.q:631
+#: src/examine.q:678
 msgid "5% Trimmed Mean"
 msgstr ""
 
 msgid "5% Trimmed Mean"
 msgstr ""
 
-#: src/examine.q:636 src/frequencies.q:102
+#: src/examine.q:689 src/frequencies.q:112
 msgid "Median"
 msgstr ""
 
 msgid "Median"
 msgstr ""
 
-#: src/examine.q:653 src/oneway.q:395 src/t-test.q:682 src/t-test.q:705
-#: src/t-test.q:829 src/t-test.q:1166
+#: src/examine.q:706 src/oneway.q:397 src/t-test.q:684 src/t-test.q:707
+#: src/t-test.q:831 src/t-test.q:1167
 msgid "Std. Deviation"
 msgstr ""
 
 msgid "Std. Deviation"
 msgstr ""
 
-#: src/examine.q:701
+#: src/examine.q:754
 msgid "Interquartile Range"
 msgstr ""
 
 msgid "Interquartile Range"
 msgstr ""
 
-#: src/examine.q:778
+#: src/examine.q:831
 msgid "Case Processing Summary"
 msgstr ""
 
 msgid "Case Processing Summary"
 msgstr ""
 
-#: src/examine.q:1103
+#: src/examine.q:1157
 msgid "Extreme Values"
 msgstr ""
 
 msgid "Extreme Values"
 msgstr ""
 
-#: src/examine.q:1126
+#: src/examine.q:1180
 msgid "Case Number"
 msgstr ""
 
 msgid "Case Number"
 msgstr ""
 
-#: src/examine.q:1252
+#: src/examine.q:1306
 msgid "Highest"
 msgstr ""
 
 msgid "Highest"
 msgstr ""
 
-#: src/examine.q:1257
+#: src/examine.q:1311
 msgid "Lowest"
 msgstr ""
 
 msgid "Lowest"
 msgstr ""
 
-#: src/file-handle.q:125
+#: src/examine.q:1350
+#, c-format
+msgid "Normal Q-Q Plot of %s"
+msgstr ""
+
+#: src/examine.q:1351 src/examine.q:1357
+msgid "Observed Value"
+msgstr ""
+
+#: src/examine.q:1352
+msgid "Expected Normal"
+msgstr ""
+
+#: src/examine.q:1355
+#, c-format
+msgid "Detrended Normal Q-Q Plot of %s"
+msgstr ""
+
+#: src/examine.q:1358
+msgid "Dev from Normal"
+msgstr ""
+
+#: src/file-handle.q:122
 #, c-format
 msgid ""
 "File handle %s already refers to file %s.  File handle cannot be redefined "
 "within a session."
 msgstr ""
 
 #, c-format
 msgid ""
 "File handle %s already refers to file %s.  File handle cannot be redefined "
 "within a session."
 msgstr ""
 
-#: src/file-handle.q:147
+#: src/file-handle.q:144
 msgid "The FILE HANDLE required subcommand NAME is not present."
 msgstr ""
 
 msgid "The FILE HANDLE required subcommand NAME is not present."
 msgstr ""
 
-#: src/file-handle.q:166
+#: src/file-handle.q:163
 msgid ""
 "Fixed-length records were specified on /RECFORM, but record length was not "
 "specified on /LRECL.  Assuming 1024-character records."
 msgstr ""
 
 msgid ""
 "Fixed-length records were specified on /RECFORM, but record length was not "
 "specified on /LRECL.  Assuming 1024-character records."
 msgstr ""
 
-#: src/file-handle.q:173
+#: src/file-handle.q:170
 #, c-format
 msgid ""
 "Record length (%ld) must be at least one byte.  1-character records will be "
 "assumed."
 msgstr ""
 
 #, c-format
 msgid ""
 "Record length (%ld) must be at least one byte.  1-character records will be "
 "assumed."
 msgstr ""
 
-#: src/file-handle.q:247
-msgid "<Inline File>"
+#: src/file-handle.q:260
+#, c-format
+msgid "Can't open %s as a %s because it is already open as a %s"
+msgstr ""
+
+#: src/file-handle.q:264
+#, c-format
+msgid "Can't open %s as a %s for %s because it is already open for %s"
+msgstr ""
+
+#: src/file-handle.q:269
+#, c-format
+msgid "Can't re-open %s as a %s for %s"
 msgstr ""
 
 msgstr ""
 
-#: src/file-handle.q:262
+#: src/file-handle.q:317
 msgid "expecting a file name or handle name"
 msgstr ""
 
 msgid "expecting a file name or handle name"
 msgstr ""
 
-#: src/frequencies.q:101
+#: src/frequencies.q:111
 msgid "S.E. Mean"
 msgstr ""
 
 msgid "S.E. Mean"
 msgstr ""
 
-#: src/frequencies.q:103
+#: src/frequencies.q:113
 msgid "Mode"
 msgstr ""
 
 msgid "Mode"
 msgstr ""
 
-#: src/frequencies.q:107
+#: src/frequencies.q:117
 msgid "S.E. Kurt"
 msgstr ""
 
 msgid "S.E. Kurt"
 msgstr ""
 
-#: src/frequencies.q:109
+#: src/frequencies.q:119
 msgid "S.E. Skew"
 msgstr ""
 
 msgid "S.E. Skew"
 msgstr ""
 
-#: src/frequencies.q:315
+#: src/frequencies.q:340
 msgid ""
 "At most one of BARCHART, HISTOGRAM, or HBAR should be given.  HBAR will be "
 "assumed.  Argument values will be given precedence increasing along the "
 "order given."
 msgstr ""
 
 msgid ""
 "At most one of BARCHART, HISTOGRAM, or HBAR should be given.  HBAR will be "
 "assumed.  Argument values will be given precedence increasing along the "
 "order given."
 msgstr ""
 
-#: src/frequencies.q:398
+#: src/frequencies.q:423
 #, c-format
 msgid ""
 "MAX must be greater than or equal to MIN, if both are specified.  However, "
 "MIN was specified as %g and MAX as %g.  MIN and MAX will be ignored."
 msgstr ""
 
 #, c-format
 msgid ""
 "MAX must be greater than or equal to MIN, if both are specified.  However, "
 "MIN was specified as %g and MAX as %g.  MIN and MAX will be ignored."
 msgstr ""
 
-#: src/frequencies.q:722
+#: src/frequencies.q:747
 msgid ""
 "Upper limit of integer mode value range must be greater than lower limit."
 msgstr ""
 
 msgid ""
 "Upper limit of integer mode value range must be greater than lower limit."
 msgstr ""
 
-#: src/frequencies.q:734
+#: src/frequencies.q:760
 #, c-format
 msgid "Variable %s specified multiple times on VARIABLES subcommand."
 msgstr ""
 
 #, c-format
 msgid "Variable %s specified multiple times on VARIABLES subcommand."
 msgstr ""
 
-#: src/frequencies.q:747
+#: src/frequencies.q:766
 #, c-format
 msgid "Integer mode specified, but %s is not a numeric variable."
 msgstr ""
 
 #, c-format
 msgid "Integer mode specified, but %s is not a numeric variable."
 msgstr ""
 
-#: src/frequencies.q:809
+#: src/frequencies.q:832
 msgid "`)' expected after GROUPED interval list."
 msgstr ""
 
 msgid "`)' expected after GROUPED interval list."
 msgstr ""
 
-#: src/frequencies.q:822
+#: src/frequencies.q:844
 #, c-format
 msgid "Variables %s specified on GROUPED but not on VARIABLES."
 msgstr ""
 
 #, c-format
 msgid "Variables %s specified on GROUPED but not on VARIABLES."
 msgstr ""
 
-#: src/frequencies.q:825
+#: src/frequencies.q:851
 #, c-format
 msgid "Variables %s specified multiple times on GROUPED subcommand."
 msgstr ""
 
 #, c-format
 msgid "Variables %s specified multiple times on GROUPED subcommand."
 msgstr ""
 
-#: src/frequencies.q:1055 src/frequencies.q:1146 src/frequencies.q:1147
-#: src/frequencies.q:1177
+#: src/frequencies.q:1082 src/frequencies.q:1174 src/frequencies.q:1175
+#: src/frequencies.q:1207
 msgid "Cum"
 msgstr ""
 
 msgid "Cum"
 msgstr ""
 
-#: src/frequencies.q:1057
+#: src/frequencies.q:1084
 msgid "Frequency"
 msgstr ""
 
 msgid "Frequency"
 msgstr ""
 
-#: src/frequencies.q:1076
+#: src/frequencies.q:1104
 msgid "Value Label"
 msgstr ""
 
 msgid "Value Label"
 msgstr ""
 
-#: src/frequencies.q:1175
+#: src/frequencies.q:1205
 msgid "Freq"
 msgstr ""
 
 msgid "Freq"
 msgstr ""
 
-#: src/frequencies.q:1176 src/frequencies.q:1178
+#: src/frequencies.q:1206 src/frequencies.q:1208
 msgid "Pct"
 msgstr ""
 
 msgid "Pct"
 msgstr ""
 
-#: src/frequencies.q:1394
+#: src/frequencies.q:1427
 #, c-format
 msgid "No valid data for variable %s; statistics not displayed."
 msgstr ""
 
 #, c-format
 msgid "No valid data for variable %s; statistics not displayed."
 msgstr ""
 
-#: src/frequencies.q:1433
+#: src/frequencies.q:1465
 msgid "Percentiles"
 msgstr ""
 
 msgid "Percentiles"
 msgstr ""
 
-#: src/list.q:148
+#: src/list.q:150
 #, c-format
 msgid ""
 "The first case (%ld) specified precedes the last case (%ld) specified.  The "
 "values will be swapped."
 msgstr ""
 
 #, c-format
 msgid ""
 "The first case (%ld) specified precedes the last case (%ld) specified.  The "
 "values will be swapped."
 msgstr ""
 
-#: src/list.q:156
+#: src/list.q:158
 #, c-format
 msgid ""
 "The first case (%ld) to list is less than 1.  The value is being reset to 1."
 msgstr ""
 
 #, c-format
 msgid ""
 "The first case (%ld) to list is less than 1.  The value is being reset to 1."
 msgstr ""
 
-#: src/list.q:162
+#: src/list.q:164
 #, c-format
 msgid ""
 "The last case (%ld) to list is less than 1.  The value is being reset to 1."
 msgstr ""
 
 #, c-format
 msgid ""
 "The last case (%ld) to list is less than 1.  The value is being reset to 1."
 msgstr ""
 
-#: src/list.q:168
+#: src/list.q:170
 #, c-format
 msgid "The step value %ld is less than 1.  The value is being reset to 1."
 msgstr ""
 
 #, c-format
 msgid "The step value %ld is less than 1.  The value is being reset to 1."
 msgstr ""
 
-#: src/list.q:195
+#: src/list.q:197
 msgid "`/FORMAT WEIGHT' specified, but weighting is not on."
 msgstr ""
 
 msgid "`/FORMAT WEIGHT' specified, but weighting is not on."
 msgstr ""
 
-#: src/list.q:436
+#: src/list.q:438
 msgid "Line"
 msgstr ""
 
 msgid "Line"
 msgstr ""
 
-#: src/means.q:101
+#: src/means.q:100
 msgid "Missing required subcommand TABLES."
 msgstr ""
 
 msgid "Missing required subcommand TABLES."
 msgstr ""
 
-#: src/means.q:135
-msgid "TABLES or CROSSBREAK subcommand may not appear more than once."
-msgstr ""
-
-#: src/means.q:172
-#, c-format
-msgid ""
-"Variable %s specified on TABLES or CROSSBREAK, but not specified on "
-"VARIABLES."
-msgstr ""
-
-#: src/means.q:186
-#, c-format
-msgid "LOWEST and HIGHEST may not be used for independent variables (%s)."
-msgstr ""
-
-#: src/means.q:194
-#, c-format
-msgid ""
-"Independent variables (%s) may not have noninteger endpoints in their ranges."
-msgstr ""
-
-#: src/means.q:227
-msgid "VARIABLES must precede TABLES."
-msgstr ""
-
-#: src/means.q:284
-#, c-format
-msgid "Upper value (%g) is less than lower value (%g) on VARIABLES subcommand."
+#: src/means.q:134
+msgid "TABLES subcommand may not appear more than once."
 msgstr ""
 
 msgstr ""
 
-#: src/oneway.q:166
+#: src/oneway.q:168
 msgid "Number of contrast coefficients must equal the number of groups"
 msgstr ""
 
 msgid "Number of contrast coefficients must equal the number of groups"
 msgstr ""
 
-#: src/oneway.q:175
+#: src/oneway.q:177
 #, c-format
 msgid "Coefficients for contrast %d do not total zero"
 msgstr ""
 
 #, c-format
 msgid "Coefficients for contrast %d do not total zero"
 msgstr ""
 
-#: src/oneway.q:240 src/t-test.q:364 src/t-test.q:449
+#: src/oneway.q:242 src/t-test.q:366 src/t-test.q:451
 #, c-format
 msgid "`%s' is not a variable name"
 msgstr ""
 
 #, c-format
 msgid "`%s' is not a variable name"
 msgstr ""
 
-#: src/oneway.q:275
+#: src/oneway.q:277
 msgid "Sum of Squares"
 msgstr ""
 
 msgid "Sum of Squares"
 msgstr ""
 
-#: src/oneway.q:277
+#: src/oneway.q:279
 msgid "Mean Square"
 msgstr ""
 
 msgid "Mean Square"
 msgstr ""
 
-#: src/oneway.q:278 src/t-test.q:976
+#: src/oneway.q:280 src/t-test.q:977
 msgid "F"
 msgstr ""
 
 msgid "F"
 msgstr ""
 
-#: src/oneway.q:279 src/oneway.q:549
+#: src/oneway.q:281 src/oneway.q:552
 msgid "Significance"
 msgstr ""
 
 msgid "Significance"
 msgstr ""
 
-#: src/oneway.q:304
+#: src/oneway.q:305
 msgid "Between Groups"
 msgstr ""
 
 msgid "Between Groups"
 msgstr ""
 
-#: src/oneway.q:305
+#: src/oneway.q:306
 msgid "Within Groups"
 msgstr ""
 
 msgid "Within Groups"
 msgstr ""
 
-#: src/oneway.q:351
+#: src/oneway.q:353
 msgid "ANOVA"
 msgstr ""
 
 msgid "ANOVA"
 msgstr ""
 
-#: src/oneway.q:546
+#: src/oneway.q:549
 msgid "Levene Statistic"
 msgstr ""
 
 msgid "Levene Statistic"
 msgstr ""
 
-#: src/oneway.q:547
+#: src/oneway.q:550
 msgid "df1"
 msgstr ""
 
 msgid "df1"
 msgstr ""
 
-#: src/oneway.q:548
+#: src/oneway.q:551
 msgid "df2"
 msgstr ""
 
 msgid "df2"
 msgstr ""
 
-#: src/oneway.q:552
+#: src/oneway.q:555
 msgid "Test of Homogeneity of Variances"
 msgstr ""
 
 msgid "Test of Homogeneity of Variances"
 msgstr ""
 
-#: src/oneway.q:628
+#: src/oneway.q:631
 msgid "Contrast Coefficients"
 msgstr ""
 
 msgid "Contrast Coefficients"
 msgstr ""
 
-#: src/oneway.q:630 src/oneway.q:707
+#: src/oneway.q:633 src/oneway.q:703
 msgid "Contrast"
 msgstr ""
 
 msgid "Contrast"
 msgstr ""
 
-#: src/oneway.q:705
+#: src/oneway.q:701
 msgid "Contrast Tests"
 msgstr ""
 
 msgid "Contrast Tests"
 msgstr ""
 
-#: src/oneway.q:708
+#: src/oneway.q:704
 msgid "Value of Contrast"
 msgstr ""
 
 msgid "Value of Contrast"
 msgstr ""
 
-#: src/oneway.q:710 src/t-test.q:978 src/t-test.q:1170 src/t-test.q:1262
+#: src/oneway.q:706 src/t-test.q:979 src/t-test.q:1171 src/t-test.q:1263
 msgid "t"
 msgstr ""
 
 msgid "t"
 msgstr ""
 
-#: src/oneway.q:712 src/t-test.q:980 src/t-test.q:1172 src/t-test.q:1264
+#: src/oneway.q:708 src/t-test.q:981 src/t-test.q:1173 src/t-test.q:1265
 msgid "Sig. (2-tailed)"
 msgstr ""
 
 msgid "Sig. (2-tailed)"
 msgstr ""
 
-#: src/oneway.q:760
+#: src/oneway.q:754
 msgid "Assume equal variances"
 msgstr ""
 
 msgid "Assume equal variances"
 msgstr ""
 
-#: src/oneway.q:764
+#: src/oneway.q:758
 msgid "Does not assume equal"
 msgstr ""
 
 msgid "Does not assume equal"
 msgstr ""
 
@@ -4748,115 +4624,115 @@ msgstr ""
 msgid "data> "
 msgstr ""
 
 msgid "data> "
 msgstr ""
 
-#: src/t-test.q:266
+#: src/t-test.q:268
 msgid "TESTVAL, GROUPS and PAIRS subcommands are mutually exclusive."
 msgstr ""
 
 msgid "TESTVAL, GROUPS and PAIRS subcommands are mutually exclusive."
 msgstr ""
 
-#: src/t-test.q:283
+#: src/t-test.q:285
 msgid "VARIABLES subcommand is not appropriate with PAIRS"
 msgstr ""
 
 msgid "VARIABLES subcommand is not appropriate with PAIRS"
 msgstr ""
 
-#: src/t-test.q:320
+#: src/t-test.q:322
 msgid "One or more VARIABLES must be specified."
 msgstr ""
 
 msgid "One or more VARIABLES must be specified."
 msgstr ""
 
-#: src/t-test.q:377
+#: src/t-test.q:379
 #, c-format
 msgid "Long string variable %s is not valid here."
 msgstr ""
 
 #, c-format
 msgid "Long string variable %s is not valid here."
 msgstr ""
 
-#: src/t-test.q:397
+#: src/t-test.q:399
 msgid ""
 "When applying GROUPS to a string variable, at least one value must be "
 "specified."
 msgstr ""
 
 msgid ""
 "When applying GROUPS to a string variable, at least one value must be "
 "specified."
 msgstr ""
 
-#: src/t-test.q:484
+#: src/t-test.q:486
 #, c-format
 msgid ""
 "PAIRED was specified but the number of variables preceding WITH (%d) did not "
 "match the number following (%d)."
 msgstr ""
 
 #, c-format
 msgid ""
 "PAIRED was specified but the number of variables preceding WITH (%d) did not "
 "match the number following (%d)."
 msgstr ""
 
-#: src/t-test.q:501
+#: src/t-test.q:503
 msgid "At least two variables must be specified on PAIRS."
 msgstr ""
 
 msgid "At least two variables must be specified on PAIRS."
 msgstr ""
 
-#: src/t-test.q:678
+#: src/t-test.q:680
 msgid "One-Sample Statistics"
 msgstr ""
 
 msgid "One-Sample Statistics"
 msgstr ""
 
-#: src/t-test.q:683 src/t-test.q:706 src/t-test.q:830
+#: src/t-test.q:685 src/t-test.q:708 src/t-test.q:832
 msgid "SE. Mean"
 msgstr ""
 
 msgid "SE. Mean"
 msgstr ""
 
-#: src/t-test.q:700
+#: src/t-test.q:702
 msgid "Group Statistics"
 msgstr ""
 
 msgid "Group Statistics"
 msgstr ""
 
-#: src/t-test.q:824
+#: src/t-test.q:826
 msgid "Paired Sample Statistics"
 msgstr ""
 
 msgid "Paired Sample Statistics"
 msgstr ""
 
-#: src/t-test.q:846 src/t-test.q:1191 src/t-test.q:1382
+#: src/t-test.q:848 src/t-test.q:1192 src/t-test.q:1382
 #, c-format
 msgid "Pair %d"
 msgstr ""
 
 #, c-format
 msgid "Pair %d"
 msgstr ""
 
-#: src/t-test.q:964
+#: src/t-test.q:965
 msgid "Independent Samples Test"
 msgstr ""
 
 msgid "Independent Samples Test"
 msgstr ""
 
-#: src/t-test.q:972
+#: src/t-test.q:973
 msgid "Levene's Test for Equality of Variances"
 msgstr ""
 
 msgid "Levene's Test for Equality of Variances"
 msgstr ""
 
-#: src/t-test.q:974
+#: src/t-test.q:975
 msgid "t-test for Equality of Means"
 msgstr ""
 
 msgid "t-test for Equality of Means"
 msgstr ""
 
-#: src/t-test.q:977 src/t-test.q:1367
+#: src/t-test.q:978 src/t-test.q:1367
 msgid "Sig."
 msgstr ""
 
 msgid "Sig."
 msgstr ""
 
-#: src/t-test.q:981 src/t-test.q:1265
+#: src/t-test.q:982 src/t-test.q:1266
 msgid "Mean Difference"
 msgstr ""
 
 msgid "Mean Difference"
 msgstr ""
 
-#: src/t-test.q:982
+#: src/t-test.q:983
 msgid "Std. Error Difference"
 msgstr ""
 
 msgid "Std. Error Difference"
 msgstr ""
 
-#: src/t-test.q:987 src/t-test.q:1162 src/t-test.q:1257
+#: src/t-test.q:988 src/t-test.q:1163 src/t-test.q:1258
 #, c-format
 msgid "%g%% Confidence Interval of the Difference"
 msgstr ""
 
 #, c-format
 msgid "%g%% Confidence Interval of the Difference"
 msgstr ""
 
-#: src/t-test.q:1041
+#: src/t-test.q:1043
 msgid "Equal variances assumed"
 msgstr ""
 
 msgid "Equal variances assumed"
 msgstr ""
 
-#: src/t-test.q:1094
+#: src/t-test.q:1095
 msgid "Equal variances not assumed"
 msgstr ""
 
 msgid "Equal variances not assumed"
 msgstr ""
 
-#: src/t-test.q:1152
+#: src/t-test.q:1153
 msgid "Paired Samples Test"
 msgstr ""
 
 msgid "Paired Samples Test"
 msgstr ""
 
-#: src/t-test.q:1155
+#: src/t-test.q:1156
 msgid "Paired Differences"
 msgstr ""
 
 msgid "Paired Differences"
 msgstr ""
 
-#: src/t-test.q:1167
+#: src/t-test.q:1168
 msgid "Std. Error Mean"
 msgstr ""
 
 msgid "Std. Error Mean"
 msgstr ""
 
-#: src/t-test.q:1246
+#: src/t-test.q:1247
 msgid "One-Sample Test"
 msgstr ""
 
 msgid "One-Sample Test"
 msgstr ""
 
-#: src/t-test.q:1251
+#: src/t-test.q:1252
 #, c-format
 msgid "Test Value = %f"
 msgstr ""
 #, c-format
 msgid "Test Value = %f"
 msgstr ""
index d10649f3fdf61da480d3ee5f9e1c4c5f342581e8..21887e86eee6260224bd7c2d9de62098c3699d9d 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
-"POT-Creation-Date: 2004-11-09 08:18+0800\n"
+"POT-Creation-Date: 2004-11-15 00:28-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"
 "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,91 +17,91 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
 
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
 
-#: src/aggregate.c:188 src/aggregate.c:223 src/data-list.c:1437
-#: src/data-list.c:1472 src/data-list.c:1485 src/data-list.c:1498
-#: src/data-list.c:1531
+#: src/aggregate.c:187 src/aggregate.c:220 src/data-list.c:1421
+#: src/data-list.c:1456 src/data-list.c:1469 src/data-list.c:1482
+#: src/data-list.c:1515
 #, c-format
 msgid "%s subcommand given multiple times."
 msgstr ""
 
 #, c-format
 msgid "%s subcommand given multiple times."
 msgstr ""
 
-#: src/aggregate.c:208
+#: src/aggregate.c:205
 msgid "while expecting COLUMNWISE"
 msgstr ""
 
 msgid "while expecting COLUMNWISE"
 msgstr ""
 
-#: src/aggregate.c:246
+#: src/aggregate.c:243
 msgid "BREAK subcommand not specified."
 msgstr ""
 
 msgid "BREAK subcommand not specified."
 msgstr ""
 
-#: src/aggregate.c:416
+#: src/aggregate.c:397
 msgid "expecting aggregation function"
 msgstr ""
 
 msgid "expecting aggregation function"
 msgstr ""
 
-#: src/aggregate.c:432
+#: src/aggregate.c:413
 #, c-format
 msgid "Unknown aggregation function %s."
 msgstr ""
 
 #, c-format
 msgid "Unknown aggregation function %s."
 msgstr ""
 
-#: src/aggregate.c:447
+#: src/aggregate.c:428
 msgid "expecting `('"
 msgstr ""
 
 msgid "expecting `('"
 msgstr ""
 
-#: src/aggregate.c:482
+#: src/aggregate.c:463
 #, c-format
 msgid "Missing argument %d to %s."
 msgstr ""
 
 #, c-format
 msgid "Missing argument %d to %s."
 msgstr ""
 
-#: src/aggregate.c:490
+#: src/aggregate.c:471
 #, c-format
 msgid "Arguments to %s must be of same type as source variables."
 msgstr ""
 
 #, c-format
 msgid "Arguments to %s must be of same type as source variables."
 msgstr ""
 
-#: src/aggregate.c:500 src/expr-prs.c:626
+#: src/aggregate.c:481 src/expr-prs.c:627
 msgid "expecting `)'"
 msgstr ""
 
 msgid "expecting `)'"
 msgstr ""
 
-#: src/aggregate.c:512
+#: src/aggregate.c:493
 #, c-format
 msgid ""
 "Number of source variables (%d) does not match number of target variables (%"
 "d)."
 msgstr ""
 
 #, c-format
 msgid ""
 "Number of source variables (%d) does not match number of target variables (%"
 "d)."
 msgstr ""
 
-#: src/aggregate.c:581
+#: src/aggregate.c:562
 #, c-format
 msgid ""
 "Variable name %s is not unique within the aggregate file dictionary, which "
 "contains the aggregate variables and the break variables."
 msgstr ""
 
 #, c-format
 msgid ""
 "Variable name %s is not unique within the aggregate file dictionary, which "
 "contains the aggregate variables and the break variables."
 msgstr ""
 
-#: src/apply-dict.c:65
+#: src/apply-dict.c:68
 #, c-format
 msgid "Variable %s is %s in target file, but %s in source file."
 msgstr ""
 
 #, c-format
 msgid "Variable %s is %s in target file, but %s in source file."
 msgstr ""
 
-#: src/apply-dict.c:68 src/apply-dict.c:69 src/expr-prs.c:1288
-#: src/expr-prs.c:1304 src/formats.c:96 src/pfm-read.c:636 src/print.c:689
-#: src/sfm-read.c:927 src/sfm-read.c:1057 src/sfm-read.c:1058
+#: src/apply-dict.c:71 src/apply-dict.c:72 src/expr-prs.c:1289
+#: src/expr-prs.c:1305 src/formats.c:96 src/pfm-read.c:604 src/print.c:695
+#: src/sfm-read.c:888 src/sfm-read.c:1017 src/sfm-read.c:1018
 msgid "string"
 msgstr ""
 
 msgid "string"
 msgstr ""
 
-#: src/apply-dict.c:68 src/apply-dict.c:69 src/expr-prs.c:1285
-#: src/expr-prs.c:1302 src/formats.c:96 src/pfm-read.c:636 src/print.c:689
-#: src/sfm-read.c:927 src/sfm-read.c:1057 src/sfm-read.c:1058
+#: src/apply-dict.c:71 src/apply-dict.c:72 src/expr-prs.c:1286
+#: src/expr-prs.c:1303 src/formats.c:96 src/pfm-read.c:604 src/print.c:695
+#: src/sfm-read.c:888 src/sfm-read.c:1017 src/sfm-read.c:1018
 msgid "numeric"
 msgstr ""
 
 msgid "numeric"
 msgstr ""
 
-#: src/apply-dict.c:81
+#: src/apply-dict.c:84
 #, c-format
 msgid "Cannot add value labels from source file to long string variable %s."
 msgstr ""
 
 #, c-format
 msgid "Cannot add value labels from source file to long string variable %s."
 msgstr ""
 
-#: src/apply-dict.c:127
+#: src/apply-dict.c:130
 #, c-format
 msgid ""
 "Cannot apply missing values from source file to long string variable %s."
 msgstr ""
 
 #, c-format
 msgid ""
 "Cannot apply missing values from source file to long string variable %s."
 msgstr ""
 
-#: src/apply-dict.c:160
+#: src/apply-dict.c:163
 msgid "No matching variables found between the source and target files."
 msgstr ""
 
 msgid "No matching variables found between the source and target files."
 msgstr ""
 
@@ -220,65 +220,65 @@ msgstr ""
 msgid "%s - Page %d"
 msgstr ""
 
 msgid "%s - Page %d"
 msgstr ""
 
-#: src/autorecode.c:121
+#: src/autorecode.c:122
 #, c-format
 msgid "Source variable count (%d) does not match target variable count (%d)."
 msgstr ""
 
 #, c-format
 msgid "Source variable count (%d) does not match target variable count (%d)."
 msgstr ""
 
-#: src/autorecode.c:138 src/command.c:738 src/compute.c:293
-#: src/data-list.c:406 src/data-list.c:903 src/data-list.c:1764
-#: src/do-if.c:253 src/get.c:351 src/lexer.c:412 src/loop.c:240
-#: src/matrix-data.c:504 src/print.c:329 src/print.c:1039 src/recode.c:404
-#: src/sel-if.c:53 src/sel-if.c:130 src/vector.c:192 src/file-handle.q:141
+#: src/autorecode.c:139 src/command.c:739 src/compute.c:294
+#: src/data-list.c:407 src/data-list.c:897 src/data-list.c:1748
+#: src/do-if.c:253 src/get.c:405 src/lexer.c:412 src/loop.c:241
+#: src/matrix-data.c:527 src/print.c:335 src/print.c:1045 src/recode.c:405
+#: src/sel-if.c:54 src/sel-if.c:131 src/vector.c:193 src/file-handle.q:138
 msgid "expecting end of command"
 msgstr ""
 
 msgid "expecting end of command"
 msgstr ""
 
-#: src/autorecode.c:148
+#: src/autorecode.c:149
 #, c-format
 msgid "Target variable %s duplicates existing variable %s."
 msgstr ""
 
 #, c-format
 msgid "Target variable %s duplicates existing variable %s."
 msgstr ""
 
-#: src/autorecode.c:155
+#: src/autorecode.c:156
 #, c-format
 msgid "Duplicate variable name %s among target variables."
 msgstr ""
 
 #, c-format
 msgid "Duplicate variable name %s among target variables."
 msgstr ""
 
-#: src/casefile.c:184
+#: src/casefile.c:182
 #, c-format
 msgid "%s: Removing temporary file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Removing temporary file: %s."
 msgstr ""
 
-#: src/casefile.c:329
+#: src/casefile.c:328
 #, c-format
 msgid "Error writing temporary file: %s."
 msgstr ""
 
 #, c-format
 msgid "Error writing temporary file: %s."
 msgstr ""
 
-#: src/casefile.c:356
+#: src/casefile.c:355
 #, c-format
 msgid "%s: Creating temporary file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Creating temporary file: %s."
 msgstr ""
 
-#: src/casefile.c:500
+#: src/casefile.c:498
 #, c-format
 msgid "%s: Opening temporary file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Opening temporary file: %s."
 msgstr ""
 
-#: src/casefile.c:526
+#: src/casefile.c:524
 #, c-format
 msgid "%s: Seeking temporary file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Seeking temporary file: %s."
 msgstr ""
 
-#: src/casefile.c:541
+#: src/casefile.c:540
 #, c-format
 msgid "%s: Reading temporary file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Reading temporary file: %s."
 msgstr ""
 
-#: src/casefile.c:544
+#: src/casefile.c:543
 #, c-format
 msgid "%s: Temporary file ended unexpectedly."
 msgstr ""
 
 #, c-format
 msgid "%s: Temporary file ended unexpectedly."
 msgstr ""
 
-#: src/cmdline.c:141 src/cmdline.c:160 src/cmdline.c:172 src/command.c:160
+#: src/cmdline.c:141 src/cmdline.c:160 src/cmdline.c:172 src/command.c:161
 #: src/set.q:414 src/set.q:416 src/set.q:956
 #, c-format
 msgid "%s is not yet implemented."
 #: src/set.q:414 src/set.q:416 src/set.q:956
 #, c-format
 msgid "%s is not yet implemented."
@@ -351,25 +351,25 @@ msgid ""
 "Report bugs to <%s>.\n"
 msgstr ""
 
 "Report bugs to <%s>.\n"
 msgstr ""
 
-#: src/command.c:98
+#: src/command.c:99
 #, c-format
 msgid "%s not allowed inside FILE TYPE/END FILE TYPE."
 msgstr ""
 
 #, c-format
 msgid "%s not allowed inside FILE TYPE/END FILE TYPE."
 msgstr ""
 
-#: src/command.c:102
+#: src/command.c:103
 #, c-format
 msgid "%s not allowed inside FILE TYPE GROUPED/END FILE TYPE."
 msgstr ""
 
 #, c-format
 msgid "%s not allowed inside FILE TYPE GROUPED/END FILE TYPE."
 msgstr ""
 
-#: src/command.c:105
+#: src/command.c:106
 msgid "RECORD TYPE must be the first command inside a FILE TYPE structure."
 msgstr ""
 
 msgid "RECORD TYPE must be the first command inside a FILE TYPE structure."
 msgstr ""
 
-#: src/command.c:150
+#: src/command.c:151
 msgid "expecting command name"
 msgstr ""
 
 msgid "expecting command name"
 msgstr ""
 
-#: src/command.c:179
+#: src/command.c:180
 #, c-format
 msgid ""
 "%s is not allowed (1) before a command to specify the input program, such as "
 #, c-format
 msgid ""
 "%s is not allowed (1) before a command to specify the input program, such as "
@@ -377,83 +377,83 @@ msgid ""
 "PROGRAM and END INPUT PROGRAM."
 msgstr ""
 
 "PROGRAM and END INPUT PROGRAM."
 msgstr ""
 
-#: src/command.c:183
+#: src/command.c:184
 #, c-format
 msgid "%s is not allowed within an input program."
 msgstr ""
 
 #, c-format
 msgid "%s is not allowed within an input program."
 msgstr ""
 
-#: src/command.c:184 src/command.c:185
+#: src/command.c:185 src/command.c:186
 #, c-format
 msgid "%s is only allowed within an input program."
 msgstr ""
 
 #, c-format
 msgid "%s is only allowed within an input program."
 msgstr ""
 
-#: src/command.c:464
+#: src/command.c:465
 #, c-format
 msgid "Unknown command %s."
 msgstr ""
 
 #, c-format
 msgid "Unknown command %s."
 msgstr ""
 
-#: src/command.c:564
+#: src/command.c:565
 msgid ""
 "This command is not accepted in a syntax file.  Instead, use FINISH to "
 "terminate a syntax file."
 msgstr ""
 
 msgid ""
 "This command is not accepted in a syntax file.  Instead, use FINISH to "
 "terminate a syntax file."
 msgstr ""
 
-#: src/command.c:582
+#: src/command.c:583
 msgid ""
 "This command is not executed in interactive mode.  Instead, PSPP drops down "
 "to the command prompt.  Use EXIT if you really want to quit."
 msgstr ""
 
 msgid ""
 "This command is not executed in interactive mode.  Instead, PSPP drops down "
 "to the command prompt.  Use EXIT if you really want to quit."
 msgstr ""
 
-#: src/command.c:625 src/command.c:756
+#: src/command.c:626 src/command.c:757
 msgid "This command not allowed when the SAFER option is set."
 msgstr ""
 
 msgid "This command not allowed when the SAFER option is set."
 msgstr ""
 
-#: src/command.c:637
+#: src/command.c:638
 #, c-format
 msgid "Error removing `%s': %s."
 msgstr ""
 
 #, c-format
 msgid "Error removing `%s': %s."
 msgstr ""
 
-#: src/command.c:687
+#: src/command.c:688
 #, c-format
 msgid "Couldn't fork: %s."
 msgstr ""
 
 #, c-format
 msgid "Couldn't fork: %s."
 msgstr ""
 
-#: src/command.c:729
+#: src/command.c:730
 #, c-format
 msgid "Error executing command: %s."
 msgstr ""
 
 #, c-format
 msgid "Error executing command: %s."
 msgstr ""
 
-#: src/command.c:777
+#: src/command.c:778
 msgid "No operating system support for this command."
 msgstr ""
 
 msgid "No operating system support for this command."
 msgstr ""
 
-#: src/command.c:800
+#: src/command.c:801
 msgid "This command is not valid in a syntax file."
 msgstr ""
 
 msgid "This command is not valid in a syntax file."
 msgstr ""
 
-#: src/compute.c:145 src/compute.c:209
+#: src/compute.c:146 src/compute.c:210
 #, c-format
 msgid ""
 "When executing COMPUTE: SYSMIS is not a valid value as an index into vector %"
 "s."
 msgstr ""
 
 #, c-format
 msgid ""
 "When executing COMPUTE: SYSMIS is not a valid value as an index into vector %"
 "s."
 msgstr ""
 
-#: src/compute.c:148 src/compute.c:213
+#: src/compute.c:149 src/compute.c:214
 #, c-format
 msgid ""
 "When executing COMPUTE: %g is not a valid value as an index into vector %s."
 msgstr ""
 
 #, c-format
 msgid ""
 "When executing COMPUTE: %g is not a valid value as an index into vector %s."
 msgstr ""
 
-#: src/compute.c:355
+#: src/compute.c:356
 #, c-format
 msgid "There is no vector named %s."
 msgstr ""
 
 #, c-format
 msgid "There is no vector named %s."
 msgstr ""
 
-#: src/count.c:155
+#: src/count.c:156
 msgid "Destination cannot be a string variable."
 msgstr ""
 
 msgid "Destination cannot be a string variable."
 msgstr ""
 
-#: src/count.c:262
+#: src/count.c:263
 #, c-format
 msgid ""
 "%g THRU %g is not a valid range.  The number following THRU must be at least "
 #, c-format
 msgid ""
 "%g THRU %g is not a valid range.  The number following THRU must be at least "
@@ -655,259 +655,259 @@ msgstr ""
 msgid "Only one of FIXED, FREE, or LIST may be specified."
 msgstr ""
 
 msgid "Only one of FIXED, FREE, or LIST may be specified."
 msgstr ""
 
-#: src/data-list.c:353 src/print.c:290
+#: src/data-list.c:354 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 ""
 
 #, 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:382 src/data-list.c:1753
+#: src/data-list.c:383 src/data-list.c:1737
 msgid ""
 "SPSS-like or FORTRAN-like format specification expected after variable names."
 msgstr ""
 
 msgid ""
 "SPSS-like or FORTRAN-like format specification expected after variable names."
 msgstr ""
 
-#: src/data-list.c:393
+#: src/data-list.c:394
 msgid "At least one variable must be specified."
 msgstr ""
 
 msgid "At least one variable must be specified."
 msgstr ""
 
-#: src/data-list.c:398 src/print.c:322
+#: src/data-list.c:399 src/print.c:328
 msgid ""
 "Variables are specified on records that should not exist according to "
 "RECORDS subcommand."
 msgstr ""
 
 msgid ""
 "Variables are specified on records that should not exist according to "
 "RECORDS subcommand."
 msgstr ""
 
-#: src/data-list.c:436 src/data-list.c:450 src/print.c:499 src/print.c:512
+#: src/data-list.c:437 src/data-list.c:451 src/print.c:505 src/print.c:518
 msgid "Column positions for fields must be positive."
 msgstr ""
 
 msgid "Column positions for fields must be positive."
 msgstr ""
 
-#: src/data-list.c:455
+#: src/data-list.c:456
 msgid "The ending column for a field must be greater than the starting column."
 msgstr ""
 
 msgid "The ending column for a field must be greater than the starting column."
 msgstr ""
 
-#: src/data-list.c:469 src/print.c:589
+#: src/data-list.c:470 src/print.c:595
 #, c-format
 msgid "The %d columns %d-%d can't be evenly divided into %d fields."
 msgstr ""
 
 #, c-format
 msgid "The %d columns %d-%d can't be evenly divided into %d fields."
 msgstr ""
 
-#: src/data-list.c:489 src/print.c:540
+#: src/data-list.c:490 src/print.c:546
 msgid "A format specifier on this line has extra characters on the end."
 msgstr ""
 
 msgid "A format specifier on this line has extra characters on the end."
 msgstr ""
 
-#: src/data-list.c:504 src/print.c:556
+#: src/data-list.c:505 src/print.c:562
 msgid "The value for number of decimal places must be at least 1."
 msgstr ""
 
 msgid "The value for number of decimal places must be at least 1."
 msgstr ""
 
-#: src/data-list.c:518 src/print.c:569
+#: src/data-list.c:519 src/print.c:575
 #, c-format
 msgid "Input format %s doesn't accept decimal places."
 msgstr ""
 
 #, c-format
 msgid "Input format %s doesn't accept decimal places."
 msgstr ""
 
-#: src/data-list.c:565 src/data-list.c:661 src/data-list.c:882
+#: src/data-list.c:566 src/data-list.c:662 src/data-list.c:876
 #, c-format
 msgid "%s is a duplicate variable name."
 msgstr ""
 
 #, c-format
 msgid "%s is a duplicate variable name."
 msgstr ""
 
-#: src/data-list.c:570
+#: src/data-list.c:571
 #, c-format
 msgid "There is already a variable %s of a different type."
 msgstr ""
 
 #, c-format
 msgid "There is already a variable %s of a different type."
 msgstr ""
 
-#: src/data-list.c:577
+#: src/data-list.c:578
 #, c-format
 msgid "There is already a string variable %s of a different width."
 msgstr ""
 
 #, c-format
 msgid "There is already a string variable %s of a different width."
 msgstr ""
 
-#: src/data-list.c:652
+#: src/data-list.c:653
 msgid ""
 "The number of format specifications exceeds the given number of variable "
 "names."
 msgstr ""
 
 msgid ""
 "The number of format specifications exceeds the given number of variable "
 "names."
 msgstr ""
 
-#: src/data-list.c:765 src/print.c:762
+#: src/data-list.c:766 src/print.c:768
 msgid ""
 "There aren't enough format specifications to match the number of variable "
 "names given."
 msgstr ""
 
 msgid ""
 "There aren't enough format specifications to match the number of variable "
 "names given."
 msgstr ""
 
-#: src/data-list.c:794 src/data-list.c:924 src/descript.c:879 src/print.c:793
-#: src/sysfile-info.c:132 src/sysfile-info.c:365 src/vfm.c:874
+#: src/data-list.c:793 src/data-list.c:919 src/descript.c:880 src/print.c:799
+#: src/sysfile-info.c:134 src/sysfile-info.c:367 src/vfm.c:875
 msgid "Variable"
 msgstr ""
 
 msgid "Variable"
 msgstr ""
 
-#: src/data-list.c:795 src/print.c:794
+#: src/data-list.c:794 src/print.c:800
 msgid "Record"
 msgstr ""
 
 msgid "Record"
 msgstr ""
 
-#: src/data-list.c:796 src/print.c:795
+#: src/data-list.c:795 src/print.c:801
 msgid "Columns"
 msgstr ""
 
 msgid "Columns"
 msgstr ""
 
-#: src/data-list.c:797 src/data-list.c:925 src/print.c:796
+#: src/data-list.c:796 src/data-list.c:920 src/print.c:802
 msgid "Format"
 msgstr ""
 
 msgid "Format"
 msgstr ""
 
-#: src/data-list.c:817
+#: src/data-list.c:812
 #, c-format
 msgid "Reading %d record from file %s."
 msgid_plural "Reading %d records from file %s."
 msgstr[0] ""
 msgstr[1] ""
 
 #, c-format
 msgid "Reading %d record from file %s."
 msgid_plural "Reading %d records from file %s."
 msgstr[0] ""
 msgstr[1] ""
 
-#: src/data-list.c:819
+#: src/data-list.c:816
 #, c-format
 msgid "Reading %d record from the command file."
 msgid_plural "Reading %d records from the command file."
 msgstr[0] ""
 msgstr[1] ""
 
 #, 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:948
+#: src/data-list.c:936
 #, c-format
 msgid "Reading free-form data from file %s."
 msgstr ""
 
 #, c-format
 msgid "Reading free-form data from file %s."
 msgstr ""
 
-#: src/data-list.c:949
+#: src/data-list.c:939
 msgid "Reading free-form data from the command file."
 msgstr ""
 
 msgid "Reading free-form data from the command file."
 msgstr ""
 
-#: src/data-list.c:1002
+#: src/data-list.c:990
 #, c-format
 msgid "Quoted string missing terminating `%c'."
 msgstr ""
 
 #, c-format
 msgid "Quoted string missing terminating `%c'."
 msgstr ""
 
-#: src/data-list.c:1111
+#: src/data-list.c:1099
 #, c-format
 msgid "Partial case of %d of %d records discarded."
 msgstr ""
 
 #, c-format
 msgid "Partial case of %d of %d records discarded."
 msgstr ""
 
-#: src/data-list.c:1165
+#: src/data-list.c:1153
 #, c-format
 msgid "Partial case discarded.  The first variable missing was %s."
 msgstr ""
 
 #, c-format
 msgid "Partial case discarded.  The first variable missing was %s."
 msgstr ""
 
-#: src/data-list.c:1209
+#: src/data-list.c:1197
 #, 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 ""
 
 #, 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:1287
+#: src/data-list.c:1275
 msgid "Attempt to read past end of file."
 msgstr ""
 
 msgid "Attempt to read past end of file."
 msgstr ""
 
-#: src/data-list.c:1427
+#: src/data-list.c:1411
 msgid ""
 "REPEATING DATA must use the same file as its corresponding DATA LIST or FILE "
 "TYPE."
 msgstr ""
 
 msgid ""
 "REPEATING DATA must use the same file as its corresponding DATA LIST or FILE "
 "TYPE."
 msgstr ""
 
-#: src/data-list.c:1461
+#: src/data-list.c:1445
 #, c-format
 msgid "STARTS beginning column (%d) exceeds STARTS ending column (%d)."
 msgstr ""
 
 #, c-format
 msgid "STARTS beginning column (%d) exceeds STARTS ending column (%d)."
 msgstr ""
 
-#: src/data-list.c:1517
+#: src/data-list.c:1501
 #, c-format
 msgid "CONTINUED beginning column (%d) exceeds CONTINUED ending column (%d)."
 msgstr ""
 
 #, c-format
 msgid "CONTINUED beginning column (%d) exceeds CONTINUED ending column (%d)."
 msgstr ""
 
-#: src/data-list.c:1540
+#: src/data-list.c:1524
 #, c-format
 msgid "ID beginning column (%ld) must be positive."
 msgstr ""
 
 #, c-format
 msgid "ID beginning column (%ld) must be positive."
 msgstr ""
 
-#: src/data-list.c:1555
+#: src/data-list.c:1539
 #, c-format
 msgid "ID ending column (%ld) must be positive."
 msgstr ""
 
 #, c-format
 msgid "ID ending column (%ld) must be positive."
 msgstr ""
 
-#: src/data-list.c:1561
+#: src/data-list.c:1545
 #, c-format
 msgid "ID ending column (%ld) cannot be less than ID beginning column (%d)."
 msgstr ""
 
 #, c-format
 msgid "ID ending column (%ld) cannot be less than ID beginning column (%d)."
 msgstr ""
 
-#: src/data-list.c:1601
+#: src/data-list.c:1585
 msgid "Missing required specification STARTS."
 msgstr ""
 
 msgid "Missing required specification STARTS."
 msgstr ""
 
-#: src/data-list.c:1603
+#: src/data-list.c:1587
 msgid "Missing required specification OCCURS."
 msgstr ""
 
 msgid "Missing required specification OCCURS."
 msgstr ""
 
-#: src/data-list.c:1610
+#: src/data-list.c:1594
 msgid "ID specified without CONTINUED."
 msgstr ""
 
 msgid "ID specified without CONTINUED."
 msgstr ""
 
-#: src/data-list.c:1702
+#: src/data-list.c:1686
 msgid "String variable not allowed here."
 msgstr ""
 
 msgid "String variable not allowed here."
 msgstr ""
 
-#: src/data-list.c:1712
+#: src/data-list.c:1696
 #, c-format
 msgid "%s (%d) must be at least 1."
 msgstr ""
 
 #, c-format
 msgid "%s (%d) must be at least 1."
 msgstr ""
 
-#: src/data-list.c:1718
+#: src/data-list.c:1702
 #, c-format
 msgid "Variable or integer expected for %s."
 msgstr ""
 
 #, c-format
 msgid "Variable or integer expected for %s."
 msgstr ""
 
-#: src/data-list.c:1856
+#: src/data-list.c:1840
 #, c-format
 msgid "Encountered mismatched record ID \"%s\" expecting \"%s\"."
 msgstr ""
 
 #, c-format
 msgid "Encountered mismatched record ID \"%s\" expecting \"%s\"."
 msgstr ""
 
-#: src/data-list.c:1888
+#: src/data-list.c:1872
 #, c-format
 msgid ""
 "Variable %s starting in column %d extends beyond physical record length of %"
 "d."
 msgstr ""
 
 #, c-format
 msgid ""
 "Variable %s starting in column %d extends beyond physical record length of %"
 "d."
 msgstr ""
 
-#: src/data-list.c:1956
+#: src/data-list.c:1940
 #, c-format
 msgid "Invalid value %d for OCCURS."
 msgstr ""
 
 #, c-format
 msgid "Invalid value %d for OCCURS."
 msgstr ""
 
-#: src/data-list.c:1962
+#: src/data-list.c:1946
 #, c-format
 msgid "Beginning column for STARTS (%d) must be at least 1."
 msgstr ""
 
 #, c-format
 msgid "Beginning column for STARTS (%d) must be at least 1."
 msgstr ""
 
-#: src/data-list.c:1970
+#: src/data-list.c:1954
 #, c-format
 msgid "Ending column for STARTS (%d) is less than beginning column (%d)."
 msgstr ""
 
 #, c-format
 msgid "Ending column for STARTS (%d) is less than beginning column (%d)."
 msgstr ""
 
-#: src/data-list.c:1978
+#: src/data-list.c:1962
 #, c-format
 msgid "Invalid value %d for LENGTH."
 msgstr ""
 
 #, c-format
 msgid "Invalid value %d for LENGTH."
 msgstr ""
 
-#: src/data-list.c:1985
+#: src/data-list.c:1969
 #, c-format
 msgid "Beginning column for CONTINUED (%d) must be at least 1."
 msgstr ""
 
 #, c-format
 msgid "Beginning column for CONTINUED (%d) must be at least 1."
 msgstr ""
 
-#: src/data-list.c:1993
+#: src/data-list.c:1977
 #, c-format
 msgid "Ending column for CONTINUED (%d) is less than beginning column (%d)."
 msgstr ""
 
 #, c-format
 msgid "Ending column for CONTINUED (%d) is less than beginning column (%d)."
 msgstr ""
 
-#: src/data-list.c:2025
+#: src/data-list.c:2009
 #, 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 ""
 
 #, 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:2043
+#: src/data-list.c:2027
 #, c-format
 msgid "Unexpected end of file with %d repetitions remaining out of %d."
 msgstr ""
 
 #, c-format
 msgid "Unexpected end of file with %d repetitions remaining out of %d."
 msgstr ""
 
-#: src/data-out.c:235 src/sfm-read.c:466 src/sysfile-info.c:113
+#: src/data-out.c:235 src/sfm-read.c:432 src/sysfile-info.c:115
 msgid "Unknown"
 msgstr ""
 
 msgid "Unknown"
 msgstr ""
 
@@ -958,99 +958,99 @@ msgstr ""
 msgid "Only USE ALL is currently implemented."
 msgstr ""
 
 msgid "Only USE ALL is currently implemented."
 msgstr ""
 
-#: src/descript.c:98 src/examine.q:584 src/frequencies.q:100 src/oneway.q:394
-#: src/t-test.q:681 src/t-test.q:704 src/t-test.q:827 src/t-test.q:1165
+#: src/descript.c:99 src/examine.q:631 src/frequencies.q:110 src/oneway.q:396
+#: src/t-test.q:683 src/t-test.q:706 src/t-test.q:829 src/t-test.q:1166
 msgid "Mean"
 msgstr ""
 
 msgid "Mean"
 msgstr ""
 
-#: src/descript.c:99
+#: src/descript.c:100
 msgid "S E Mean"
 msgstr ""
 
 msgid "S E Mean"
 msgstr ""
 
-#: src/descript.c:100 src/frequencies.q:104
+#: src/descript.c:101 src/frequencies.q:114
 msgid "Std Dev"
 msgstr ""
 
 msgid "Std Dev"
 msgstr ""
 
-#: src/descript.c:101 src/examine.q:641 src/frequencies.q:105
+#: src/descript.c:102 src/examine.q:694 src/frequencies.q:115
 msgid "Variance"
 msgstr ""
 
 msgid "Variance"
 msgstr ""
 
-#: src/descript.c:102 src/examine.q:711 src/frequencies.q:106
+#: src/descript.c:103 src/examine.q:764 src/frequencies.q:116
 msgid "Kurtosis"
 msgstr ""
 
 msgid "Kurtosis"
 msgstr ""
 
-#: src/descript.c:103
+#: src/descript.c:104
 msgid "S E Kurt"
 msgstr ""
 
 msgid "S E Kurt"
 msgstr ""
 
-#: src/descript.c:104 src/examine.q:706 src/frequencies.q:108
+#: src/descript.c:105 src/examine.q:759 src/frequencies.q:118
 msgid "Skewness"
 msgstr ""
 
 msgid "Skewness"
 msgstr ""
 
-#: src/descript.c:105
+#: src/descript.c:106
 msgid "S E Skew"
 msgstr ""
 
 msgid "S E Skew"
 msgstr ""
 
-#: src/descript.c:106 src/examine.q:689 src/frequencies.q:110
+#: src/descript.c:107 src/examine.q:742 src/frequencies.q:120
 msgid "Range"
 msgstr ""
 
 msgid "Range"
 msgstr ""
 
-#: src/descript.c:107 src/examine.q:666 src/frequencies.q:111 src/oneway.q:406
+#: src/descript.c:108 src/examine.q:719 src/frequencies.q:121 src/oneway.q:408
 msgid "Minimum"
 msgstr ""
 
 msgid "Minimum"
 msgstr ""
 
-#: src/descript.c:108 src/examine.q:677 src/frequencies.q:112 src/oneway.q:407
+#: src/descript.c:109 src/examine.q:730 src/frequencies.q:122 src/oneway.q:409
 msgid "Maximum"
 msgstr ""
 
 msgid "Maximum"
 msgstr ""
 
-#: src/descript.c:109 src/frequencies.q:113
+#: src/descript.c:110 src/frequencies.q:123
 msgid "Sum"
 msgstr ""
 
 msgid "Sum"
 msgstr ""
 
-#: src/descript.c:332
+#: src/descript.c:333
 #, c-format
 msgid "Z-score variable name %s would be a duplicate variable name."
 msgstr ""
 
 #, c-format
 msgid "Z-score variable name %s would be a duplicate variable name."
 msgstr ""
 
-#: src/descript.c:350 src/list.q:140
+#: src/descript.c:351 src/list.q:142
 msgid "No variables specified."
 msgstr ""
 
 msgid "No variables specified."
 msgstr ""
 
-#: src/descript.c:434
+#: src/descript.c:435
 msgid "expecting statistic name: reverting to default"
 msgstr ""
 
 msgid "expecting statistic name: reverting to default"
 msgstr ""
 
-#: src/descript.c:507
+#: src/descript.c:508
 msgid ""
 "Ran out of generic names for Z-score variables.  There are only 126 generic "
 "names: ZSC001-ZSC0999, STDZ01-STDZ09, ZZZZ01-ZZZZ09, ZQZQ01-ZQZQ09."
 msgstr ""
 
 msgid ""
 "Ran out of generic names for Z-score variables.  There are only 126 generic "
 "names: ZSC001-ZSC0999, STDZ01-STDZ09, ZZZZ01-ZZZZ09, ZQZQ01-ZQZQ09."
 msgstr ""
 
-#: src/descript.c:538
+#: src/descript.c:539
 msgid "Mapping of variables to corresponding Z-scores."
 msgstr ""
 
 msgid "Mapping of variables to corresponding Z-scores."
 msgstr ""
 
-#: src/descript.c:543
+#: src/descript.c:544
 msgid "Source"
 msgstr ""
 
 msgid "Source"
 msgstr ""
 
-#: src/descript.c:544
+#: src/descript.c:545
 msgid "Target"
 msgstr ""
 
 msgid "Target"
 msgstr ""
 
-#: src/descript.c:663 src/descript.c:669
+#: src/descript.c:664 src/descript.c:670
 msgid "Z-score of "
 msgstr ""
 
 msgid "Z-score of "
 msgstr ""
 
-#: src/descript.c:882
+#: src/descript.c:883
 msgid "Valid N"
 msgstr ""
 
 msgid "Valid N"
 msgstr ""
 
-#: src/descript.c:883
+#: src/descript.c:884
 msgid "Missing N"
 msgstr ""
 
 msgid "Missing N"
 msgstr ""
 
-#: src/descript.c:909
+#: src/descript.c:910
 #, c-format
 msgid "Valid cases = %g; cases with missing value(s) = %g."
 msgstr ""
 #, c-format
 msgid "Valid cases = %g; cases with missing value(s) = %g."
 msgstr ""
@@ -1075,99 +1075,58 @@ msgstr ""
 msgid "Cannot open first page on DEVIND device %s."
 msgstr ""
 
 msgid "Cannot open first page on DEVIND device %s."
 msgstr ""
 
-#: src/dfm.c:88 src/dfm.c:565
-#, c-format
-msgid "%s: Closing data-file handle %s."
-msgstr ""
-
-#: src/dfm.c:115
-#, c-format
-msgid "Cannot read from file %s already opened for %s."
-msgstr ""
-
-#: src/dfm.c:129
-#, c-format
-msgid "%s: Opening data-file handle %s for reading."
-msgstr ""
-
-#: src/dfm.c:144
+#: src/dfm-read.c:153
 #, c-format
 msgid "Could not open \"%s\" for reading as a data file: %s."
 msgstr ""
 
 #, c-format
 msgid "Could not open \"%s\" for reading as a data file: %s."
 msgstr ""
 
-#: src/dfm.c:186 src/dfm.c:204
+#: src/dfm-read.c:186 src/dfm-read.c:204
 msgid "BEGIN DATA expected."
 msgstr ""
 
 msgid "BEGIN DATA expected."
 msgstr ""
 
-#: src/dfm.c:213
+#: src/dfm-read.c:213
 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 ""
 
 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.c:241 src/dfm.c:261
+#: src/dfm-read.c:246 src/dfm-read.c:266
 #, c-format
 msgid "Error reading file %s: %s."
 msgstr ""
 
 #, c-format
 msgid "Error reading file %s: %s."
 msgstr ""
 
-#: src/dfm.c:264
+#: src/dfm-read.c:269
 #, c-format
 msgid "%s: Partial record at end of file."
 msgstr ""
 
 #, c-format
 msgid "%s: Partial record at end of file."
 msgstr ""
 
-#: src/dfm.c:300
+#: src/dfm-read.c:312
 #, c-format
 msgid "Attempt to read beyond end-of-file on file %s."
 msgstr ""
 
 #, c-format
 msgid "Attempt to read beyond end-of-file on file %s."
 msgstr ""
 
-#: src/dfm.c:446
-msgid "reading as a data file"
-msgstr ""
-
-#: src/dfm.c:472
-#, c-format
-msgid "Cannot write to file %s already opened for %s."
-msgstr ""
-
-#: src/dfm.c:485
-#, c-format
-msgid "%s: Opening data-file handle %s for writing."
+#: src/dfm-read.c:315
+msgid "Attempt to read beyond END DATA."
 msgstr ""
 
 msgstr ""
 
-#: src/dfm.c:491
-msgid "Cannot open the inline file for writing."
+#: src/dfm-read.c:462
+msgid ""
+"This command is not valid here since the current input program does not "
+"access the inline file."
 msgstr ""
 
 msgstr ""
 
-#: src/dfm.c:505
+#: src/dfm-write.c:67
 #, c-format
 msgid "An error occurred while opening \"%s\" for writing as a data file: %s."
 msgstr ""
 
 #, c-format
 msgid "An error occurred while opening \"%s\" for writing as a data file: %s."
 msgstr ""
 
-#: src/dfm.c:547
+#: src/dfm-write.c:103
 #, c-format
 msgid "Error writing file %s: %s."
 msgstr ""
 
 #, c-format
 msgid "Error writing file %s: %s."
 msgstr ""
 
-#: src/dfm.c:581
-msgid "writing as a data file"
-msgstr ""
-
-#: src/dfm.c:598
-msgid ""
-"This command is not valid here since the current input program does not "
-"access the inline file."
-msgstr ""
-
-#: src/dfm.c:605
-msgid "inline file: Opening for reading."
-msgstr ""
-
-#: src/dfm.c:618
-msgid "Skipping remaining inline data."
-msgstr ""
-
-#: src/dictionary.c:583
+#: src/dictionary.c:603
 msgid ""
 "At least one case in the data file had a weight value that was user-missing, "
 "system-missing, zero, or negative.  These case(s) were ignored."
 msgid ""
 "At least one case in the data file had a weight value that was user-missing, "
 "system-missing, zero, or negative.  These case(s) were ignored."
@@ -1236,7 +1195,7 @@ msgstr ""
 msgid "fatal"
 msgstr ""
 
 msgid "fatal"
 msgstr ""
 
-#: src/error.c:259 src/error.c:266 src/error.c:269 src/expr-prs.c:1279
+#: src/error.c:259 src/error.c:266 src/error.c:269 src/expr-prs.c:1280
 msgid "error"
 msgstr ""
 
 msgid "error"
 msgstr ""
 
@@ -1252,53 +1211,53 @@ msgstr ""
 msgid "installation error"
 msgstr ""
 
 msgid "installation error"
 msgstr ""
 
-#: src/expr-evl.c:633
+#: src/expr-evl.c:634
 msgid "TIME.HMS cannot mix positive and negative in its arguments."
 msgstr ""
 
 msgid "TIME.HMS cannot mix positive and negative in its arguments."
 msgstr ""
 
-#: src/expr-evl.c:697
+#: src/expr-evl.c:698
 msgid "Week argument to WKYR must be in range 0 to 53."
 msgstr ""
 
 msgid "Week argument to WKYR must be in range 0 to 53."
 msgstr ""
 
-#: src/expr-evl.c:848 src/expr-evl.c:904
+#: src/expr-evl.c:849 src/expr-evl.c:905
 msgid "Argument 3 of RINDEX may not be system-missing."
 msgstr ""
 
 msgid "Argument 3 of RINDEX may not be system-missing."
 msgstr ""
 
-#: src/expr-evl.c:858 src/expr-evl.c:914
+#: src/expr-evl.c:859 src/expr-evl.c:915
 msgid ""
 "Argument 3 of RINDEX must be between 1 and the length of argument 2, and it "
 "must evenly divide the length of argument 2."
 msgstr ""
 
 msgid ""
 "Argument 3 of RINDEX must be between 1 and the length of argument 2, and it "
 "must evenly divide the length of argument 2."
 msgstr ""
 
-#: src/expr-evl.c:1100
+#: src/expr-evl.c:1101
 msgid ""
 "A number being treated as a Boolean in an expression was found to have a "
 "value other than 0 (false), 1 (true), or the system-missing value.  The "
 "result was forced to 0."
 msgstr ""
 
 msgid ""
 "A number being treated as a Boolean in an expression was found to have a "
 "value other than 0 (false), 1 (true), or the system-missing value.  The "
 "result was forced to 0."
 msgstr ""
 
-#: src/expr-evl.c:1141
+#: src/expr-evl.c:1142
 #, c-format
 msgid ""
 "SYSMIS is not a valid index value for vector %s.  The result will be set to "
 "SYSMIS."
 msgstr ""
 
 #, c-format
 msgid ""
 "SYSMIS is not a valid index value for vector %s.  The result will be set to "
 "SYSMIS."
 msgstr ""
 
-#: src/expr-evl.c:1145
+#: src/expr-evl.c:1146
 #, c-format
 msgid ""
 "%g is not a valid index value for vector %s.  The result will be set to "
 "SYSMIS."
 msgstr ""
 
 #, c-format
 msgid ""
 "%g is not a valid index value for vector %s.  The result will be set to "
 "SYSMIS."
 msgstr ""
 
-#: src/expr-evl.c:1164
+#: src/expr-evl.c:1165
 #, c-format
 msgid ""
 "SYSMIS is not a valid index value for vector %s.  The result will be set to "
 "the empty string."
 msgstr ""
 
 #, c-format
 msgid ""
 "SYSMIS is not a valid index value for vector %s.  The result will be set to "
 "the empty string."
 msgstr ""
 
-#: src/expr-evl.c:1169
+#: src/expr-evl.c:1170
 #, c-format
 msgid ""
 "%g is not a valid index value for vector %s.  The result will be set to the "
 #, c-format
 msgid ""
 "%g is not a valid index value for vector %s.  The result will be set to the "
@@ -1311,29 +1270,29 @@ msgid ""
 "Boolean value was found to have a constant value other than 0, 1, or SYSMIS."
 msgstr ""
 
 "Boolean value was found to have a constant value other than 0, 1, or SYSMIS."
 msgstr ""
 
-#: src/expr-prs.c:137
+#: src/expr-prs.c:138
 msgid ""
 "Type mismatch: expression has string type, but a numeric value is required "
 "here."
 msgstr ""
 
 msgid ""
 "Type mismatch: expression has string type, but a numeric value is required "
 "here."
 msgstr ""
 
-#: src/expr-prs.c:148
+#: src/expr-prs.c:149
 msgid ""
 "Type mismatch: expression has numeric type, but a string value is required "
 "here."
 msgstr ""
 
 msgid ""
 "Type mismatch: expression has numeric type, but a string value is required "
 "here."
 msgstr ""
 
-#: src/expr-prs.c:207
+#: src/expr-prs.c:208
 #, c-format
 msgid "Type mismatch: operands of %s operator must be strings."
 msgstr ""
 
 #, c-format
 msgid "Type mismatch: operands of %s operator must be strings."
 msgstr ""
 
-#: src/expr-prs.c:210
+#: src/expr-prs.c:211
 #, c-format
 msgid "Type mismatch: operands of %s operator must be numeric."
 msgstr ""
 
 #, c-format
 msgid "Type mismatch: operands of %s operator must be numeric."
 msgstr ""
 
-#: src/expr-prs.c:391
+#: src/expr-prs.c:392
 msgid ""
 "Chaining relational operators (e.g. \"a < b < c\") will not produce the "
 "mathematically expected result.  Use the AND logical operator to fix the "
 msgid ""
 "Chaining relational operators (e.g. \"a < b < c\") will not produce the "
 "mathematically expected result.  Use the AND logical operator to fix the "
@@ -1341,336 +1300,336 @@ msgid ""
 "parentheses will disable this warning (e.g. \"(a < b) < c\".)"
 msgstr ""
 
 "parentheses will disable this warning (e.g. \"(a < b) < c\".)"
 msgstr ""
 
-#: src/expr-prs.c:471
+#: src/expr-prs.c:472
 msgid ""
 "The exponentiation operator (\"**\") is left-associative, even though right-"
 "associative semantics are more useful.  That is, \"a**b**c\" equals \"(a**b)"
 "**c\", not as \"a**(b**c)\".  To disable this warning, insert parentheses."
 msgstr ""
 
 msgid ""
 "The exponentiation operator (\"**\") is left-associative, even though right-"
 "associative semantics are more useful.  That is, \"a**b**c\" equals \"(a**b)"
 "**c\", not as \"a**(b**c)\".  To disable this warning, insert parentheses."
 msgstr ""
 
-#: src/expr-prs.c:552
+#: src/expr-prs.c:553
 #, c-format
 msgid "Unknown system variable %s."
 msgstr ""
 
 #, c-format
 msgid "Unknown system variable %s."
 msgstr ""
 
-#: src/expr-prs.c:591
+#: src/expr-prs.c:592
 msgid "expecting variable name"
 msgstr ""
 
 msgid "expecting variable name"
 msgstr ""
 
-#: src/expr-prs.c:634
+#: src/expr-prs.c:635
 msgid "in expression"
 msgstr ""
 
 msgid "in expression"
 msgstr ""
 
-#: src/expr-prs.c:730
+#: src/expr-prs.c:731
 msgid "Argument 2 to LAG must be a small positive integer constant."
 msgstr ""
 
 msgid "Argument 2 to LAG must be a small positive integer constant."
 msgstr ""
 
-#: src/expr-prs.c:811 src/expr-prs.c:850
+#: src/expr-prs.c:812 src/expr-prs.c:851
 #, c-format
 msgid ""
 "Type mismatch in argument %d of %s, which was expected to be of %s type.  It "
 "was actually of %s type. "
 msgstr ""
 
 #, c-format
 msgid ""
 "Type mismatch in argument %d of %s, which was expected to be of %s type.  It "
 "was actually of %s type. "
 msgstr ""
 
-#: src/expr-prs.c:837
+#: src/expr-prs.c:838
 #, c-format
 msgid "%s cannot take Boolean operands."
 msgstr ""
 
 #, c-format
 msgid "%s cannot take Boolean operands."
 msgstr ""
 
-#: src/expr-prs.c:869
+#: src/expr-prs.c:870
 msgid "in function call"
 msgstr ""
 
 msgid "in function call"
 msgstr ""
 
-#: src/expr-prs.c:883
+#: src/expr-prs.c:884
 msgid "RANGE requires an odd number of arguments, but at least three."
 msgstr ""
 
 msgid "RANGE requires an odd number of arguments, but at least three."
 msgstr ""
 
-#: src/expr-prs.c:893
+#: src/expr-prs.c:894
 #, c-format
 msgid "%s requires at least two arguments."
 msgstr ""
 
 #, c-format
 msgid "%s requires at least two arguments."
 msgstr ""
 
-#: src/expr-prs.c:908
+#: src/expr-prs.c:909
 #, c-format
 msgid "%s.%d requires at least %d arguments."
 msgstr ""
 
 #, c-format
 msgid "%s.%d requires at least %d arguments."
 msgstr ""
 
-#: src/expr-prs.c:973
+#: src/expr-prs.c:974
 #, c-format
 msgid ""
 "Argument %d to CONCAT is type %s.  All arguments to CONCAT must be strings."
 msgstr ""
 
 #, c-format
 msgid ""
 "Argument %d to CONCAT is type %s.  All arguments to CONCAT must be strings."
 msgstr ""
 
-#: src/expr-prs.c:1070
+#: src/expr-prs.c:1071
 #, c-format
 msgid ""
 "Argument %d to %s was expected to be of %s type.  It was actually of type %s."
 msgstr ""
 
 #, c-format
 msgid ""
 "Argument %d to %s was expected to be of %s type.  It was actually of type %s."
 msgstr ""
 
-#: src/expr-prs.c:1087
+#: src/expr-prs.c:1088
 #, c-format
 msgid "%s is not a numeric format."
 msgstr ""
 
 #, c-format
 msgid "%s is not a numeric format."
 msgstr ""
 
-#: src/expr-prs.c:1125
+#: src/expr-prs.c:1126
 #, c-format
 msgid "Too few arguments to function %s."
 msgstr ""
 
 #, c-format
 msgid "Too few arguments to function %s."
 msgstr ""
 
-#: src/expr-prs.c:1158
+#: src/expr-prs.c:1159
 #, c-format
 msgid ""
 "Type mismatch in argument %d of %s.  A string expression was supplied where "
 "only a numeric expression is allowed."
 msgstr ""
 
 #, c-format
 msgid ""
 "Type mismatch in argument %d of %s.  A string expression was supplied where "
 "only a numeric expression is allowed."
 msgstr ""
 
-#: src/expr-prs.c:1168
+#: src/expr-prs.c:1169
 #, c-format
 msgid "Missing comma following argument %d of %s."
 msgstr ""
 
 #, c-format
 msgid "Missing comma following argument %d of %s."
 msgstr ""
 
-#: src/expr-prs.c:1206
+#: src/expr-prs.c:1207
 msgid "The index value after a vector name must be numeric."
 msgstr ""
 
 msgid "The index value after a vector name must be numeric."
 msgstr ""
 
-#: src/expr-prs.c:1213
+#: src/expr-prs.c:1214
 msgid "`)' expected after a vector index value."
 msgstr ""
 
 msgid "`)' expected after a vector index value."
 msgstr ""
 
-#: src/expr-prs.c:1246
+#: src/expr-prs.c:1247
 #, c-format
 msgid "There is no function named %s."
 msgstr ""
 
 #, c-format
 msgid "There is no function named %s."
 msgstr ""
 
-#: src/expr-prs.c:1251
+#: src/expr-prs.c:1252
 #, c-format
 msgid "Function %s may not be given a minimum number of arguments."
 msgstr ""
 
 #, c-format
 msgid "Function %s may not be given a minimum number of arguments."
 msgstr ""
 
-#: src/expr-prs.c:1260
+#: src/expr-prs.c:1261
 #, c-format
 msgid "expecting `)' after %s function"
 msgstr ""
 
 #, c-format
 msgid "expecting `)' after %s function"
 msgstr ""
 
-#: src/expr-prs.c:1282
+#: src/expr-prs.c:1283
 msgid "Boolean"
 msgstr ""
 
 msgid "Boolean"
 msgstr ""
 
-#: src/filename.c:221
-#, c-format
-msgid "Searching for `%s'..."
-msgstr ""
-
-#: src/filename.c:229 src/filename.c:261
-msgid "Search unsuccessful!"
-msgstr ""
-
-#: src/filename.c:254
-#, c-format
-msgid "Found `%s'."
-msgstr ""
-
-#: src/filename.c:686
-#, c-format
-msgid "Not opening pipe file `%s' because SAFER option set."
-msgstr ""
-
-#: src/file-type.c:127
+#: src/file-type.c:129
 msgid "MIXED, GROUPED, or NESTED expected."
 msgstr ""
 
 msgid "MIXED, GROUPED, or NESTED expected."
 msgstr ""
 
-#: src/file-type.c:150
+#: src/file-type.c:152
 msgid "The CASE subcommand is not valid on FILE TYPE MIXED."
 msgstr ""
 
 msgid "The CASE subcommand is not valid on FILE TYPE MIXED."
 msgstr ""
 
-#: src/file-type.c:168
+#: src/file-type.c:170
 msgid "WARN or NOWARN expected after WILD."
 msgstr ""
 
 msgid "WARN or NOWARN expected after WILD."
 msgstr ""
 
-#: src/file-type.c:176
+#: src/file-type.c:178
 msgid "The DUPLICATE subcommand is not valid on FILE TYPE MIXED."
 msgstr ""
 
 msgid "The DUPLICATE subcommand is not valid on FILE TYPE MIXED."
 msgstr ""
 
-#: src/file-type.c:190
+#: src/file-type.c:192
 msgid "DUPLICATE=CASE is only valid on FILE TYPE NESTED."
 msgstr ""
 
 msgid "DUPLICATE=CASE is only valid on FILE TYPE NESTED."
 msgstr ""
 
-#: src/file-type.c:199
+#: src/file-type.c:201
 #, c-format
 msgid "WARN%s expected after DUPLICATE."
 msgstr ""
 
 #, c-format
 msgid "WARN%s expected after DUPLICATE."
 msgstr ""
 
-#: src/file-type.c:200
+#: src/file-type.c:202
 msgid ", NOWARN, or CASE"
 msgstr ""
 
 msgid ", NOWARN, or CASE"
 msgstr ""
 
-#: src/file-type.c:201
+#: src/file-type.c:203
 msgid " or NOWARN"
 msgstr ""
 
 msgid " or NOWARN"
 msgstr ""
 
-#: src/file-type.c:209
+#: src/file-type.c:211
 msgid "The MISSING subcommand is not valid on FILE TYPE MIXED."
 msgstr ""
 
 msgid "The MISSING subcommand is not valid on FILE TYPE MIXED."
 msgstr ""
 
-#: src/file-type.c:221
+#: src/file-type.c:223
 msgid "WARN or NOWARN after MISSING."
 msgstr ""
 
 msgid "WARN or NOWARN after MISSING."
 msgstr ""
 
-#: src/file-type.c:229
+#: src/file-type.c:231
 msgid "ORDERED is only valid on FILE TYPE GROUPED."
 msgstr ""
 
 msgid "ORDERED is only valid on FILE TYPE GROUPED."
 msgstr ""
 
-#: src/file-type.c:240
+#: src/file-type.c:242
 msgid "YES or NO expected after ORDERED."
 msgstr ""
 
 msgid "YES or NO expected after ORDERED."
 msgstr ""
 
-#: src/file-type.c:246 src/file-type.c:540 src/get.c:335
+#: src/file-type.c:248 src/file-type.c:543 src/get.c:389
 msgid "while expecting a valid subcommand"
 msgstr ""
 
 msgid "while expecting a valid subcommand"
 msgstr ""
 
-#: src/file-type.c:253
+#: src/file-type.c:255
 msgid "The required RECORD subcommand was not present."
 msgstr ""
 
 msgid "The required RECORD subcommand was not present."
 msgstr ""
 
-#: src/file-type.c:261
+#: src/file-type.c:263
 msgid "The required CASE subcommand was not present."
 msgstr ""
 
 msgid "The required CASE subcommand was not present."
 msgstr ""
 
-#: src/file-type.c:267
+#: src/file-type.c:269
 msgid "CASE and RECORD must specify different variable names."
 msgstr ""
 
 msgid "CASE and RECORD must specify different variable names."
 msgstr ""
 
-#: src/file-type.c:324
+#: src/file-type.c:327
 msgid "Column value must be positive."
 msgstr ""
 
 msgid "Column value must be positive."
 msgstr ""
 
-#: src/file-type.c:340
+#: src/file-type.c:343
 msgid "Ending column precedes beginning column."
 msgstr ""
 
 msgid "Ending column precedes beginning column."
 msgstr ""
 
-#: src/file-type.c:360
+#: src/file-type.c:363
 msgid "Bad format specifier name."
 msgstr ""
 
 msgid "Bad format specifier name."
 msgstr ""
 
-#: src/file-type.c:389 src/file-type.c:577
+#: src/file-type.c:392 src/file-type.c:580
 msgid ""
 "This command may only appear within a FILE TYPE/END FILE TYPE structure."
 msgstr ""
 
 msgid ""
 "This command may only appear within a FILE TYPE/END FILE TYPE structure."
 msgstr ""
 
-#: src/file-type.c:412
+#: src/file-type.c:415
 msgid "OTHER may appear only on the last RECORD TYPE command."
 msgstr ""
 
 msgid "OTHER may appear only on the last RECORD TYPE command."
 msgstr ""
 
-#: src/file-type.c:422
+#: src/file-type.c:425
 msgid "No input commands (DATA LIST, REPEATING DATA) for above RECORD TYPE."
 msgstr ""
 
 msgid "No input commands (DATA LIST, REPEATING DATA) for above RECORD TYPE."
 msgstr ""
 
-#: src/file-type.c:473
+#: src/file-type.c:476
 msgid ""
 "The CASE subcommand is not allowed on the RECORD TYPE command for FILE TYPE "
 "MIXED."
 msgstr ""
 
 msgid ""
 "The CASE subcommand is not allowed on the RECORD TYPE command for FILE TYPE "
 "MIXED."
 msgstr ""
 
-#: src/file-type.c:483
+#: src/file-type.c:486
 msgid ""
 "No variable name may be specified for the CASE subcommand on RECORD TYPE."
 msgstr ""
 
 msgid ""
 "No variable name may be specified for the CASE subcommand on RECORD TYPE."
 msgstr ""
 
-#: src/file-type.c:491
+#: src/file-type.c:494
 msgid ""
 "The CASE column specification on RECORD TYPE must give a format specifier "
 "that is the same type as that of the CASE column specification given on FILE "
 "TYPE."
 msgstr ""
 
 msgid ""
 "The CASE column specification on RECORD TYPE must give a format specifier "
 "that is the same type as that of the CASE column specification given on FILE "
 "TYPE."
 msgstr ""
 
-#: src/file-type.c:507
+#: src/file-type.c:510
 msgid "WARN or NOWARN expected on DUPLICATE subcommand."
 msgstr ""
 
 msgid "WARN or NOWARN expected on DUPLICATE subcommand."
 msgstr ""
 
-#: src/file-type.c:521
+#: src/file-type.c:524
 msgid "WARN or NOWARN expected on MISSING subcommand."
 msgstr ""
 
 msgid "WARN or NOWARN expected on MISSING subcommand."
 msgstr ""
 
-#: src/file-type.c:534
+#: src/file-type.c:537
 msgid "YES or NO expected on SPREAD subcommand."
 msgstr ""
 
 msgid "YES or NO expected on SPREAD subcommand."
 msgstr ""
 
-#: src/file-type.c:590
+#: src/file-type.c:593
 msgid "No input commands (DATA LIST, REPEATING DATA) on above RECORD TYPE."
 msgstr ""
 
 msgid "No input commands (DATA LIST, REPEATING DATA) on above RECORD TYPE."
 msgstr ""
 
-#: src/file-type.c:597
+#: src/file-type.c:600
 msgid "No commands between FILE TYPE and END FILE TYPE."
 msgstr ""
 
 msgid "No commands between FILE TYPE and END FILE TYPE."
 msgstr ""
 
-#: src/file-type.c:666
+#: src/file-type.c:669
 #, c-format
 msgid "Unknown record type \"%.*s\"."
 msgstr ""
 
 #, c-format
 msgid "Unknown record type \"%.*s\"."
 msgstr ""
 
-#: src/file-type.c:690
+#: src/file-type.c:693
 #, c-format
 msgid "Unknown record type %g."
 msgstr ""
 
 #, c-format
 msgid "Unknown record type %g."
 msgstr ""
 
-#: src/flip.c:77
+#: src/filename.c:221
+#, c-format
+msgid "Searching for `%s'..."
+msgstr ""
+
+#: src/filename.c:229 src/filename.c:261
+msgid "Search unsuccessful!"
+msgstr ""
+
+#: src/filename.c:254
+#, c-format
+msgid "Found `%s'."
+msgstr ""
+
+#: src/filename.c:686
+#, c-format
+msgid "Not opening pipe file `%s' because SAFER option set."
+msgstr ""
+
+#: src/flip.c:78
 msgid ""
 "FLIP ignores TEMPORARY.  Temporary transformations will be made permanent."
 msgstr ""
 
 msgid ""
 "FLIP ignores TEMPORARY.  Temporary transformations will be made permanent."
 msgstr ""
 
-#: src/flip.c:217
+#: src/flip.c:218
 #, c-format
 msgid "Could not create acceptable variant for variable %s."
 msgstr ""
 
 #, c-format
 msgid "Could not create acceptable variant for variable %s."
 msgstr ""
 
-#: src/flip.c:233
+#: src/flip.c:234
 msgid "Cannot create more than 99999 variable names."
 msgstr ""
 
 msgid "Cannot create more than 99999 variable names."
 msgstr ""
 
-#: src/flip.c:277
+#: src/flip.c:278
 msgid "Could not create temporary file for FLIP."
 msgstr ""
 
 msgid "Could not create temporary file for FLIP."
 msgstr ""
 
-#: src/flip.c:284 src/flip.c:352
+#: src/flip.c:285 src/flip.c:353
 #, c-format
 msgid "Error writing FLIP file: %s."
 msgstr ""
 
 #, c-format
 msgid "Error writing FLIP file: %s."
 msgstr ""
 
-#: src/flip.c:394
+#: src/flip.c:395
 #, c-format
 msgid "Error rewinding FLIP file: %s."
 msgstr ""
 
 #, c-format
 msgid "Error rewinding FLIP file: %s."
 msgstr ""
 
-#: src/flip.c:398
+#: src/flip.c:399
 msgid "Error creating FLIP source file."
 msgstr ""
 
 msgid "Error creating FLIP source file."
 msgstr ""
 
-#: src/flip.c:407
+#: src/flip.c:408
 #, c-format
 msgid "Error reading FLIP file: %s."
 msgstr ""
 
 #, c-format
 msgid "Error reading FLIP file: %s."
 msgstr ""
 
-#: src/flip.c:424
+#: src/flip.c:425
 #, c-format
 msgid "Error seeking FLIP source file: %s."
 msgstr ""
 
 #, c-format
 msgid "Error seeking FLIP source file: %s."
 msgstr ""
 
-#: src/flip.c:429
+#: src/flip.c:430
 #, c-format
 msgid "Error writing FLIP source file: %s."
 msgstr ""
 
 #, c-format
 msgid "Error writing FLIP source file: %s."
 msgstr ""
 
-#: src/flip.c:440
+#: src/flip.c:441
 #, c-format
 msgid "Error rewind FLIP source file: %s."
 msgstr ""
 
 #, c-format
 msgid "Error rewind FLIP source file: %s."
 msgstr ""
 
-#: src/flip.c:492
+#: src/flip.c:493
 #, c-format
 msgid "Error reading FLIP temporary file: %s."
 msgstr ""
 
 #, c-format
 msgid "Error reading FLIP temporary file: %s."
 msgstr ""
 
-#: src/flip.c:495
+#: src/flip.c:496
 msgid "Unexpected end of file reading FLIP temporary file."
 msgstr ""
 
 msgid "Unexpected end of file reading FLIP temporary file."
 msgstr ""
 
@@ -1765,15 +1724,15 @@ msgstr ""
 msgid "Format %s may not be assigned to a %s variable."
 msgstr ""
 
 msgid "Format %s may not be assigned to a %s variable."
 msgstr ""
 
-#: src/formats.c:116 src/numeric.c:64 src/numeric.c:135
+#: src/formats.c:116 src/numeric.c:65 src/numeric.c:136
 msgid "`)' expected after output format."
 msgstr ""
 
 msgid "`)' expected after output format."
 msgstr ""
 
-#: src/get.c:341
+#: src/get.c:395
 msgid "All variables deleted from system file dictionary."
 msgstr ""
 
 msgid "All variables deleted from system file dictionary."
 msgstr ""
 
-#: src/get.c:388
+#: src/get.c:445
 #, c-format
 msgid ""
 "Cannot rename %s as %s because there already exists a variable named %s.  To "
 #, c-format
 msgid ""
 "Cannot rename %s as %s because there already exists a variable named %s.  To "
@@ -1781,78 +1740,78 @@ msgid ""
 "as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, \"/RENAME (A B C=B C A)\"."
 msgstr ""
 
 "as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, \"/RENAME (A B C=B C A)\"."
 msgstr ""
 
-#: src/get.c:413
+#: src/get.c:470
 msgid "`=' expected after variable list."
 msgstr ""
 
 msgid "`=' expected after variable list."
 msgstr ""
 
-#: src/get.c:420
+#: src/get.c:477
 #, 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 ""
 
 #, 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:433
+#: src/get.c:490
 #, c-format
 msgid "Requested renaming duplicates variable name %s."
 msgstr ""
 
 #, c-format
 msgid "Requested renaming duplicates variable name %s."
 msgstr ""
 
-#: src/get.c:564
+#: src/get.c:674
 msgid "The BY subcommand may be given once at most."
 msgstr ""
 
 msgid "The BY subcommand may be given once at most."
 msgstr ""
 
-#: src/get.c:631
+#: src/get.c:746
 msgid "The active file may not be specified more than once."
 msgstr ""
 
 msgid "The active file may not be specified more than once."
 msgstr ""
 
-#: src/get.c:640
+#: src/get.c:755
 msgid "Cannot specify the active file since no active file has been defined."
 msgstr ""
 
 msgid "Cannot specify the active file since no active file has been defined."
 msgstr ""
 
-#: src/get.c:648
+#: src/get.c:763
 msgid ""
 "MATCH FILES may not be used after TEMPORARY when the active file is an input "
 "source.  Temporary transformations will be made permanent."
 msgstr ""
 
 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:682
+#: src/get.c:793
 msgid ""
 "IN, FIRST, and LAST subcommands may not occur before the first FILE or TABLE."
 msgstr ""
 
 msgid ""
 "IN, FIRST, and LAST subcommands may not occur before the first FILE or TABLE."
 msgstr ""
 
-#: src/get.c:717
+#: src/get.c:828
 #, c-format
 msgid "Multiple %s subcommands for a single FILE or TABLE."
 msgstr ""
 
 #, c-format
 msgid "Multiple %s subcommands for a single FILE or TABLE."
 msgstr ""
 
-#: src/get.c:727
+#: src/get.c:838
 #, c-format
 msgid "Duplicate variable name %s while creating %s variable."
 msgstr ""
 
 #, c-format
 msgid "Duplicate variable name %s while creating %s variable."
 msgstr ""
 
-#: src/get.c:741
+#: src/get.c:850
 msgid ""
 "RENAME, KEEP, and DROP subcommands may not occur before the first FILE or "
 "TABLE."
 msgstr ""
 
 msgid ""
 "RENAME, KEEP, and DROP subcommands may not occur before the first FILE or "
 "TABLE."
 msgstr ""
 
-#: src/get.c:765
+#: src/get.c:877
 msgid "The BY subcommand is required when a TABLE subcommand is given."
 msgstr ""
 
 msgid "The BY subcommand is required when a TABLE subcommand is given."
 msgstr ""
 
-#: src/get.c:786
+#: src/get.c:896
 #, c-format
 msgid "File %s lacks BY variable %s."
 msgstr ""
 
 #, c-format
 msgid "File %s lacks BY variable %s."
 msgstr ""
 
-#: src/get.c:1282
+#: src/get.c:1390
 #, c-format
 msgid ""
 "Variable %s in file %s (%s) has different type or width from the same "
 "variable in earlier file (%s)."
 msgstr ""
 
 #, 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:1330
+#: src/get.c:1468
 msgid "expecting COMM or TAPE"
 msgstr ""
 
 msgid "expecting COMM or TAPE"
 msgstr ""
 
@@ -2073,7 +2032,7 @@ msgstr ""
 msgid "HTML output driver: %s: %s"
 msgstr ""
 
 msgid "HTML output driver: %s: %s"
 msgstr ""
 
-#: src/html.c:403 src/list.q:250
+#: src/html.c:403 src/list.q:252
 #, c-format
 msgid "Cannot open first page on HTML device %s."
 msgstr ""
 #, c-format
 msgid "Cannot open first page on HTML device %s."
 msgstr ""
@@ -2082,27 +2041,27 @@ msgstr ""
 msgid "expecting filename"
 msgstr ""
 
 msgid "expecting filename"
 msgstr ""
 
-#: src/inpt-pgm.c:81
+#: src/inpt-pgm.c:82
 msgid "No matching INPUT PROGRAM command."
 msgstr ""
 
 msgid "No matching INPUT PROGRAM command."
 msgstr ""
 
-#: src/inpt-pgm.c:86
+#: src/inpt-pgm.c:87
 msgid ""
 "No data-input or transformation commands specified between INPUT PROGRAM and "
 "END INPUT PROGRAM."
 msgstr ""
 
 msgid ""
 "No data-input or transformation commands specified between INPUT PROGRAM and "
 "END INPUT PROGRAM."
 msgstr ""
 
-#: src/inpt-pgm.c:287 src/inpt-pgm.c:420
+#: src/inpt-pgm.c:288 src/inpt-pgm.c:418
 msgid ""
 "This command may only be executed between INPUT PROGRAM and END INPUT "
 "PROGRAM."
 msgstr ""
 
 msgid ""
 "This command may only be executed between INPUT PROGRAM and END INPUT "
 "PROGRAM."
 msgstr ""
 
-#: src/inpt-pgm.c:342
+#: src/inpt-pgm.c:338
 msgid "COLUMN subcommand multiply specified."
 msgstr ""
 
 msgid "COLUMN subcommand multiply specified."
 msgstr ""
 
-#: src/inpt-pgm.c:395
+#: src/inpt-pgm.c:391
 msgid ""
 "REREAD: Column numbers must be positive finite numbers.  Column set to 1."
 msgstr ""
 msgid ""
 "REREAD: Column numbers must be positive finite numbers.  Column set to 1."
 msgstr ""
@@ -2166,7 +2125,7 @@ msgstr ""
 msgid "<ERROR>"
 msgstr ""
 
 msgid "<ERROR>"
 msgstr ""
 
-#: src/lexer.c:993 src/pfm-read.c:136 src/repeat.c:213
+#: src/lexer.c:993 src/pfm-read.c:134 src/repeat.c:214
 msgid "Unexpected end of file."
 msgstr ""
 
 msgid "Unexpected end of file."
 msgstr ""
 
@@ -2207,232 +2166,232 @@ msgid ""
 "spaces."
 msgstr ""
 
 "spaces."
 msgstr ""
 
-#: src/loop.c:193
+#: src/loop.c:194
 msgid "The index variable may not be a string variable."
 msgstr ""
 
 msgid "The index variable may not be a string variable."
 msgstr ""
 
-#: src/loop.c:299
+#: src/loop.c:300
 msgid "There is no LOOP command that corresponds to this END LOOP."
 msgstr ""
 
 msgid "There is no LOOP command that corresponds to this END LOOP."
 msgstr ""
 
-#: src/loop.c:493
+#: src/loop.c:494
 msgid ""
 "This command may only appear enclosed in a LOOP/END LOOP control structure."
 msgstr ""
 
 msgid ""
 "This command may only appear enclosed in a LOOP/END LOOP control structure."
 msgstr ""
 
-#: src/loop.c:499
+#: src/loop.c:500
 msgid "BREAK not enclosed in DO IF structure."
 msgstr ""
 
 msgid "BREAK not enclosed in DO IF structure."
 msgstr ""
 
-#: src/loop.c:577
+#: src/loop.c:578
 #, c-format
 msgid "%s without %s."
 msgstr ""
 
 #, c-format
 msgid "%s without %s."
 msgstr ""
 
-#: src/main.c:74
+#: src/main.c:76
 msgid "Error initializing output drivers."
 msgstr ""
 
 msgid "Error initializing output drivers."
 msgstr ""
 
-#: src/main.c:140
+#: src/main.c:146
 msgid "This command not executed."
 msgstr ""
 
 msgid "This command not executed."
 msgstr ""
 
-#: src/main.c:144
+#: src/main.c:150
 msgid ""
 "Skipping the rest of this command.  Part of this command may have been "
 "executed."
 msgstr ""
 
 msgid ""
 "Skipping the rest of this command.  Part of this command may have been "
 "executed."
 msgstr ""
 
-#: src/main.c:149
+#: src/main.c:155
 msgid ""
 "Skipping the rest of this command.  This command was fully executed up to "
 "this point."
 msgstr ""
 
 msgid ""
 "Skipping the rest of this command.  This command was fully executed up to "
 "this point."
 msgstr ""
 
-#: src/main.c:154
+#: src/main.c:160
 msgid ""
 "Trailing garbage was encountered following this command.  The command was "
 "fully executed to this point."
 msgstr ""
 
 msgid ""
 "Trailing garbage was encountered following this command.  The command was "
 "fully executed to this point."
 msgstr ""
 
-#: src/main.c:171
+#: src/main.c:177
 msgid "The rest of this command has been discarded."
 msgstr ""
 
 msgid "The rest of this command has been discarded."
 msgstr ""
 
-#: src/matrix-data.c:185
+#: src/matrix-data.c:208
 msgid "VARIABLES subcommand multiply specified."
 msgstr ""
 
 msgid "VARIABLES subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:200
+#: src/matrix-data.c:223
 msgid "VARNAME_ cannot be explicitly specified on VARIABLES."
 msgstr ""
 
 msgid "VARNAME_ cannot be explicitly specified on VARIABLES."
 msgstr ""
 
-#: src/matrix-data.c:265
+#: src/matrix-data.c:284
 msgid "in FORMAT subcommand"
 msgstr ""
 
 msgid "in FORMAT subcommand"
 msgstr ""
 
-#: src/matrix-data.c:276
+#: src/matrix-data.c:295
 msgid "SPLIT subcommand multiply specified."
 msgstr ""
 
 msgid "SPLIT subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:283
+#: src/matrix-data.c:302
 msgid "in SPLIT subcommand"
 msgstr ""
 
 msgid "in SPLIT subcommand"
 msgstr ""
 
-#: src/matrix-data.c:292
+#: src/matrix-data.c:311
 msgid "Split variable may not be named ROWTYPE_ or VARNAME_."
 msgstr ""
 
 msgid "Split variable may not be named ROWTYPE_ or VARNAME_."
 msgstr ""
 
-#: src/matrix-data.c:325
+#: src/matrix-data.c:345
 #, c-format
 msgid "Split variable %s is already another type."
 msgstr ""
 
 #, c-format
 msgid "Split variable %s is already another type."
 msgstr ""
 
-#: src/matrix-data.c:340
+#: src/matrix-data.c:360
 msgid "FACTORS subcommand multiply specified."
 msgstr ""
 
 msgid "FACTORS subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:355
+#: src/matrix-data.c:378
 #, c-format
 msgid "Factor variable %s is already another type."
 msgstr ""
 
 #, c-format
 msgid "Factor variable %s is already another type."
 msgstr ""
 
-#: src/matrix-data.c:370
+#: src/matrix-data.c:393
 msgid "CELLS subcommand multiply specified."
 msgstr ""
 
 msgid "CELLS subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:376 src/matrix-data.c:395
+#: src/matrix-data.c:399 src/matrix-data.c:418
 msgid "expecting positive integer"
 msgstr ""
 
 msgid "expecting positive integer"
 msgstr ""
 
-#: src/matrix-data.c:389
+#: src/matrix-data.c:412
 msgid "N subcommand multiply specified."
 msgstr ""
 
 msgid "N subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:410
+#: src/matrix-data.c:433
 msgid "CONTENTS subcommand multiply specified."
 msgstr ""
 
 msgid "CONTENTS subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:430
+#: src/matrix-data.c:453
 msgid "Nested parentheses not allowed."
 msgstr ""
 
 msgid "Nested parentheses not allowed."
 msgstr ""
 
-#: src/matrix-data.c:440
+#: src/matrix-data.c:463
 msgid "Mismatched right parenthesis (`(')."
 msgstr ""
 
 msgid "Mismatched right parenthesis (`(')."
 msgstr ""
 
-#: src/matrix-data.c:445
+#: src/matrix-data.c:468
 msgid "Empty parentheses not allowed."
 msgstr ""
 
 msgid "Empty parentheses not allowed."
 msgstr ""
 
-#: src/matrix-data.c:458 src/matrix-data.c:466
+#: src/matrix-data.c:481 src/matrix-data.c:489
 msgid "in CONTENTS subcommand"
 msgstr ""
 
 msgid "in CONTENTS subcommand"
 msgstr ""
 
-#: src/matrix-data.c:473
+#: src/matrix-data.c:496
 #, c-format
 msgid "Content multiply specified for %s."
 msgstr ""
 
 #, c-format
 msgid "Content multiply specified for %s."
 msgstr ""
 
-#: src/matrix-data.c:490
+#: src/matrix-data.c:513
 msgid "Missing right parenthesis."
 msgstr ""
 
 msgid "Missing right parenthesis."
 msgstr ""
 
-#: src/matrix-data.c:510
+#: src/matrix-data.c:533
 msgid "Missing VARIABLES subcommand."
 msgstr ""
 
 msgid "Missing VARIABLES subcommand."
 msgstr ""
 
-#: src/matrix-data.c:516
+#: src/matrix-data.c:539
 msgid ""
 "CONTENTS subcommand not specified: assuming file contains only CORR matrix."
 msgstr ""
 
 msgid ""
 "CONTENTS subcommand not specified: assuming file contains only CORR matrix."
 msgstr ""
 
-#: src/matrix-data.c:526
+#: src/matrix-data.c:549
 msgid ""
 "Missing CELLS subcommand.  CELLS is required when ROWTYPE_ is not given in "
 "the data and factors are present."
 msgstr ""
 
 msgid ""
 "Missing CELLS subcommand.  CELLS is required when ROWTYPE_ is not given in "
 "the data and factors are present."
 msgstr ""
 
-#: src/matrix-data.c:534
+#: src/matrix-data.c:557
 msgid "Split file values must be present in the data when ROWTYPE_ is present."
 msgstr ""
 
 msgid "Split file values must be present in the data when ROWTYPE_ is present."
 msgstr ""
 
-#: src/matrix-data.c:589
+#: src/matrix-data.c:610
 msgid "No continuous variables specified."
 msgstr ""
 
 msgid "No continuous variables specified."
 msgstr ""
 
-#: src/matrix-data.c:815
+#: src/matrix-data.c:853
 msgid "Scope of string exceeds line."
 msgstr ""
 
 msgid "Scope of string exceeds line."
 msgstr ""
 
-#: src/matrix-data.c:882
+#: src/matrix-data.c:920
 #, c-format
 msgid "End of line expected %s while reading %s."
 msgstr ""
 
 #, c-format
 msgid "End of line expected %s while reading %s."
 msgstr ""
 
-#: src/matrix-data.c:1070
+#: src/matrix-data.c:1106
 #, c-format
 msgid "expecting value for %s %s"
 msgstr ""
 
 #, c-format
 msgid "expecting value for %s %s"
 msgstr ""
 
-#: src/matrix-data.c:1232
+#: src/matrix-data.c:1270
 #, c-format
 msgid "Syntax error expecting SPLIT FILE value %s."
 msgstr ""
 
 #, c-format
 msgid "Syntax error expecting SPLIT FILE value %s."
 msgstr ""
 
-#: src/matrix-data.c:1241
+#: src/matrix-data.c:1279
 #, c-format
 msgid "Expecting value %g for %s."
 msgstr ""
 
 #, c-format
 msgid "Expecting value %g for %s."
 msgstr ""
 
-#: src/matrix-data.c:1282 src/matrix-data.c:1750
+#: src/matrix-data.c:1320 src/matrix-data.c:1787
 #, c-format
 msgid "Syntax error expecting factor value %s."
 msgstr ""
 
 #, c-format
 msgid "Syntax error expecting factor value %s."
 msgstr ""
 
-#: src/matrix-data.c:1291
+#: src/matrix-data.c:1329
 #, c-format
 msgid "Syntax error expecting value %g for %s %s."
 msgstr ""
 
 #, c-format
 msgid "Syntax error expecting value %g for %s %s."
 msgstr ""
 
-#: src/matrix-data.c:1527
+#: src/matrix-data.c:1564
 #, c-format
 msgid "Syntax error %s expecting SPLIT FILE value."
 msgstr ""
 
 #, c-format
 msgid "Syntax error %s expecting SPLIT FILE value."
 msgstr ""
 
-#: src/matrix-data.c:1657
+#: src/matrix-data.c:1694
 #, c-format
 msgid ""
 "Expected %d lines of data for %s content; actually saw %d lines.  No data "
 "will be output for this content."
 msgstr ""
 
 #, c-format
 msgid ""
 "Expected %d lines of data for %s content; actually saw %d lines.  No data "
 "will be output for this content."
 msgstr ""
 
-#: src/matrix-data.c:1692
+#: src/matrix-data.c:1729
 #, c-format
 msgid "Multiply specified ROWTYPE_ %s."
 msgstr ""
 
 #, c-format
 msgid "Multiply specified ROWTYPE_ %s."
 msgstr ""
 
-#: src/matrix-data.c:1697
+#: src/matrix-data.c:1734
 #, c-format
 msgid "Syntax error %s expecting ROWTYPE_ string."
 msgstr ""
 
 #, c-format
 msgid "Syntax error %s expecting ROWTYPE_ string."
 msgstr ""
 
-#: src/matrix-data.c:1717
+#: src/matrix-data.c:1754
 #, c-format
 msgid "Syntax error %s."
 msgstr ""
 
 #, c-format
 msgid "Syntax error %s."
 msgstr ""
 
-#: src/matrix-data.c:1867
+#: src/matrix-data.c:1904
 #, c-format
 msgid "Duplicate specification for %s."
 msgstr ""
 
 #, c-format
 msgid "Duplicate specification for %s."
 msgstr ""
 
-#: src/matrix-data.c:1879
+#: src/matrix-data.c:1916
 #, c-format
 msgid "Too many rows of matrix data for %s."
 msgstr ""
 
 #, c-format
 msgid "Too many rows of matrix data for %s."
 msgstr ""
 
-#: src/matrix-data.c:1927
+#: src/matrix-data.c:1964
 #, c-format
 msgid "Syntax error expecting value for %s %s."
 msgstr ""
 #, c-format
 msgid "Syntax error expecting value for %s %s."
 msgstr ""
@@ -2475,79 +2434,79 @@ msgstr ""
 msgid "String is not of proper length."
 msgstr ""
 
 msgid "String is not of proper length."
 msgstr ""
 
-#: src/mis-val.c:316 src/repeat.c:459
+#: src/mis-val.c:316 src/repeat.c:460
 msgid "String expected."
 msgstr ""
 
 msgid "String expected."
 msgstr ""
 
-#: src/modify-vars.c:88
+#: src/modify-vars.c:89
 msgid ""
 "MODIFY VARS may not be used after TEMPORARY.  Temporary transformations will "
 "be made permanent."
 msgstr ""
 
 msgid ""
 "MODIFY VARS may not be used after TEMPORARY.  Temporary transformations will "
 "be made permanent."
 msgstr ""
 
-#: src/modify-vars.c:112
+#: src/modify-vars.c:113
 msgid "REORDER subcommand may be given at most once."
 msgstr ""
 
 msgid "REORDER subcommand may be given at most once."
 msgstr ""
 
-#: src/modify-vars.c:135
+#: src/modify-vars.c:136
 msgid "Cannot specify ALL after specifying a set of variables."
 msgstr ""
 
 msgid "Cannot specify ALL after specifying a set of variables."
 msgstr ""
 
-#: src/modify-vars.c:145
+#: src/modify-vars.c:146
 msgid "`(' expected on REORDER subcommand."
 msgstr ""
 
 msgid "`(' expected on REORDER subcommand."
 msgstr ""
 
-#: src/modify-vars.c:157
+#: src/modify-vars.c:158
 msgid "`)' expected following variable names on REORDER subcommand."
 msgstr ""
 
 msgid "`)' expected following variable names on REORDER subcommand."
 msgstr ""
 
-#: src/modify-vars.c:175
+#: src/modify-vars.c:176
 msgid "RENAME subcommand may be given at most once."
 msgstr ""
 
 msgid "RENAME subcommand may be given at most once."
 msgstr ""
 
-#: src/modify-vars.c:188
+#: src/modify-vars.c:189
 msgid "`(' expected on RENAME subcommand."
 msgstr ""
 
 msgid "`(' expected on RENAME subcommand."
 msgstr ""
 
-#: src/modify-vars.c:196
+#: src/modify-vars.c:197
 msgid ""
 "`=' expected between lists of new and old variable names on RENAME "
 "subcommand."
 msgstr ""
 
 msgid ""
 "`=' expected between lists of new and old variable names on RENAME "
 "subcommand."
 msgstr ""
 
-#: src/modify-vars.c:204 src/rename-vars.c:74
+#: src/modify-vars.c:205 src/rename-vars.c:75
 #, c-format
 msgid ""
 "Differing number of variables in old name list (%d) and in new name list (%"
 "d)."
 msgstr ""
 
 #, c-format
 msgid ""
 "Differing number of variables in old name list (%d) and in new name list (%"
 "d)."
 msgstr ""
 
-#: src/modify-vars.c:215
+#: src/modify-vars.c:216
 msgid "`)' expected after variable lists on RENAME subcommand."
 msgstr ""
 
 msgid "`)' expected after variable lists on RENAME subcommand."
 msgstr ""
 
-#: src/modify-vars.c:229
+#: src/modify-vars.c:230
 msgid ""
 "KEEP subcommand may be given at most once.  It may notbe given in "
 "conjunction with the DROP subcommand."
 msgstr ""
 
 msgid ""
 "KEEP subcommand may be given at most once.  It may notbe given in "
 "conjunction with the DROP subcommand."
 msgstr ""
 
-#: src/modify-vars.c:271
+#: src/modify-vars.c:272
 msgid ""
 "DROP subcommand may be given at most once.  It may not be given in "
 "conjunction with the KEEP subcommand."
 msgstr ""
 
 msgid ""
 "DROP subcommand may be given at most once.  It may not be given in "
 "conjunction with the KEEP subcommand."
 msgstr ""
 
-#: src/modify-vars.c:297
+#: src/modify-vars.c:298
 #, c-format
 msgid "Unrecognized subcommand name `%s'."
 msgstr ""
 
 #, c-format
 msgid "Unrecognized subcommand name `%s'."
 msgstr ""
 
-#: src/modify-vars.c:299
+#: src/modify-vars.c:300
 msgid "Subcommand name expected."
 msgstr ""
 
 msgid "Subcommand name expected."
 msgstr ""
 
-#: src/modify-vars.c:307
+#: src/modify-vars.c:308
 msgid "`/' or `.' expected."
 msgstr ""
 
 msgid "`/' or `.' expected."
 msgstr ""
 
@@ -2555,17 +2514,17 @@ msgstr ""
 msgid "expecting weight value"
 msgstr ""
 
 msgid "expecting weight value"
 msgstr ""
 
-#: src/numeric.c:57
+#: src/numeric.c:58
 #, c-format
 msgid "Format type %s may not be used with a numeric variable."
 msgstr ""
 
 #, c-format
 msgid "Format type %s may not be used with a numeric variable."
 msgstr ""
 
-#: src/numeric.c:76 src/numeric.c:158 src/vector.c:155
+#: src/numeric.c:77 src/numeric.c:159 src/vector.c:156
 #, c-format
 msgid "There is already a variable named %s."
 msgstr ""
 
 #, c-format
 msgid "There is already a variable named %s."
 msgstr ""
 
-#: src/numeric.c:128
+#: src/numeric.c:129
 #, c-format
 msgid "Format type %s may not be used with a string variable."
 msgstr ""
 #, c-format
 msgid "Format type %s may not be used with a string variable."
 msgstr ""
@@ -2750,223 +2709,179 @@ msgstr ""
 msgid "portable file %s corrupt at offset %ld: "
 msgstr ""
 
 msgid "portable file %s corrupt at offset %ld: "
 msgstr ""
 
-#: src/pfm-read.c:111 src/pfm-write.c:504
+#: src/pfm-read.c:114 src/pfm-write.c:490
 #, c-format
 msgid "%s: Closing portable file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Closing portable file: %s."
 msgstr ""
 
-#: src/pfm-read.c:144
+#: src/pfm-read.c:142
 msgid "Bad line end."
 msgstr ""
 
 msgid "Bad line end."
 msgstr ""
 
-#: src/pfm-read.c:225
-#, c-format
-msgid "Cannot read file %s as portable file: already opened for %s."
-msgstr ""
-
-#: src/pfm-read.c:231
-#, c-format
-msgid "%s: Opening portable-file handle %s for reading."
-msgstr ""
-
-#: src/pfm-read.c:239
+#: src/pfm-read.c:233
 #, c-format
 msgid ""
 "An error occurred while opening \"%s\" for reading as a portable file: %s."
 msgstr ""
 
 #, c-format
 msgid ""
 "An error occurred while opening \"%s\" for reading as a portable file: %s."
 msgstr ""
 
-#: src/pfm-read.c:274
+#: src/pfm-read.c:256
 msgid "Data record expected."
 msgstr ""
 
 msgid "Data record expected."
 msgstr ""
 
-#: src/pfm-read.c:276
-msgid "Read portable-file dictionary successfully."
-msgstr ""
-
-#: src/pfm-read.c:282
-msgid "Error reading portable-file dictionary."
-msgstr ""
-
-#: src/pfm-read.c:380
+#: src/pfm-read.c:353
 msgid "Missing numeric terminator."
 msgstr ""
 
 msgid "Missing numeric terminator."
 msgstr ""
 
-#: src/pfm-read.c:417
+#: src/pfm-read.c:390
 msgid "Bad integer format."
 msgstr ""
 
 msgid "Bad integer format."
 msgstr ""
 
-#: src/pfm-read.c:447
+#: src/pfm-read.c:419
 #, c-format
 msgid "Bad string length %d."
 msgstr ""
 
 #, c-format
 msgid "Bad string length %d."
 msgstr ""
 
-#: src/pfm-read.c:546
+#: src/pfm-read.c:514
 #, c-format
 msgid "Bad date string length %d."
 msgstr ""
 
 #, c-format
 msgid "Bad date string length %d."
 msgstr ""
 
-#: src/pfm-read.c:550
+#: src/pfm-read.c:518
 msgid "Bad character in date."
 msgstr ""
 
 msgid "Bad character in date."
 msgstr ""
 
-#: src/pfm-read.c:570
+#: src/pfm-read.c:538
 #, c-format
 msgid "Bad time string length %d."
 msgstr ""
 
 #, c-format
 msgid "Bad time string length %d."
 msgstr ""
 
-#: src/pfm-read.c:574
+#: src/pfm-read.c:542
 msgid "Bad character in time."
 msgstr ""
 
 msgid "Bad character in time."
 msgstr ""
 
-#: src/pfm-read.c:624 src/pfm-read.c:631 src/sfm-read.c:912 src/sfm-read.c:920
+#: src/pfm-read.c:592 src/pfm-read.c:599 src/sfm-read.c:873 src/sfm-read.c:881
 #, c-format
 msgid "%s: Bad format specifier byte (%d)."
 msgstr ""
 
 #, c-format
 msgid "%s: Bad format specifier byte (%d)."
 msgstr ""
 
-#: src/pfm-read.c:633
+#: src/pfm-read.c:601
 #, c-format
 msgid "%s variable %s has %s format specifier %s."
 msgstr ""
 
 #, c-format
 msgid "%s variable %s has %s format specifier %s."
 msgstr ""
 
-#: src/pfm-read.c:634 src/print.c:601 src/sfm-read.c:925
+#: src/pfm-read.c:602 src/print.c:607 src/sfm-read.c:886
 msgid "String"
 msgstr ""
 
 msgid "String"
 msgstr ""
 
-#: src/pfm-read.c:634 src/print.c:601 src/sfm-read.c:925
+#: src/pfm-read.c:602 src/print.c:607 src/sfm-read.c:886
 msgid "Numeric"
 msgstr ""
 
 msgid "Numeric"
 msgstr ""
 
-#: src/pfm-read.c:673
+#: src/pfm-read.c:640
 msgid "Expected variable count record."
 msgstr ""
 
 msgid "Expected variable count record."
 msgstr ""
 
-#: src/pfm-read.c:677
+#: src/pfm-read.c:644
 #, c-format
 msgid "Invalid number of variables %d."
 msgstr ""
 
 #, c-format
 msgid "Invalid number of variables %d."
 msgstr ""
 
-#: src/pfm-read.c:687
+#: src/pfm-read.c:654
 #, c-format
 msgid "Unexpected flag value %d."
 msgstr ""
 
 #, c-format
 msgid "Unexpected flag value %d."
 msgstr ""
 
-#: src/pfm-read.c:701
+#: src/pfm-read.c:666
 #, c-format
 msgid "Weight variable name (%s) truncated."
 msgstr ""
 
 #, c-format
 msgid "Weight variable name (%s) truncated."
 msgstr ""
 
-#: src/pfm-read.c:716
+#: src/pfm-read.c:681
 msgid "Expected variable record."
 msgstr ""
 
 msgid "Expected variable record."
 msgstr ""
 
-#: src/pfm-read.c:722
+#: src/pfm-read.c:687
 #, c-format
 msgid "Invalid variable width %d."
 msgstr ""
 
 #, c-format
 msgid "Invalid variable width %d."
 msgstr ""
 
-#: src/pfm-read.c:740
+#: src/pfm-read.c:705
 #, c-format
 msgid "position %d: Variable name has %u characters."
 msgstr ""
 
 #, c-format
 msgid "position %d: Variable name has %u characters."
 msgstr ""
 
-#: src/pfm-read.c:744
+#: src/pfm-read.c:709
 #, c-format
 msgid "position %d: Variable name begins with invalid character."
 msgstr ""
 
 #, c-format
 msgid "position %d: Variable name begins with invalid character."
 msgstr ""
 
-#: src/pfm-read.c:748
+#: src/pfm-read.c:713
 #, c-format
 msgid "position %d: Variable name begins with lowercase letter %c."
 msgstr ""
 
 #, c-format
 msgid "position %d: Variable name begins with lowercase letter %c."
 msgstr ""
 
-#: src/pfm-read.c:761
+#: src/pfm-read.c:726
 #, c-format
 msgid "position %d: Variable name character %d is lowercase letter %c."
 msgstr ""
 
 #, c-format
 msgid "position %d: Variable name character %d is lowercase letter %c."
 msgstr ""
 
-#: src/pfm-read.c:771
+#: src/pfm-read.c:736
 #, c-format
 msgid "position %d: character `\\%03o' is not valid in a variable name."
 msgstr ""
 
 #, c-format
 msgid "position %d: character `\\%03o' is not valid in a variable name."
 msgstr ""
 
-#: src/pfm-read.c:782
+#: src/pfm-read.c:748
 #, c-format
 msgid "Duplicate variable name %s."
 msgstr ""
 
 #, c-format
 msgid "Duplicate variable name %s."
 msgstr ""
 
-#: src/pfm-read.c:826
+#: src/pfm-read.c:792
 #, c-format
 msgid "Bad missing values for %s."
 msgstr ""
 
 #, c-format
 msgid "Bad missing values for %s."
 msgstr ""
 
-#: src/pfm-read.c:849
+#: src/pfm-read.c:815
 #, c-format
 msgid "Weighting variable %s not present in dictionary."
 msgstr ""
 
 #, c-format
 msgid "Weighting variable %s not present in dictionary."
 msgstr ""
 
-#: src/pfm-read.c:922
+#: src/pfm-read.c:886
 #, c-format
 msgid "Unknown variable %s while parsing value labels."
 msgstr ""
 
 #, c-format
 msgid "Unknown variable %s while parsing value labels."
 msgstr ""
 
-#: src/pfm-read.c:925
+#: src/pfm-read.c:889
 #, c-format
 msgid ""
 "Cannot assign value labels to %s and %s, which have different variable types "
 "or widths."
 msgstr ""
 
 #, c-format
 msgid ""
 "Cannot assign value labels to %s and %s, which have different variable types "
 "or widths."
 msgstr ""
 
-#: src/pfm-read.c:958
+#: src/pfm-read.c:922
 #, c-format
 msgid "Duplicate label for value %g for variable %s."
 msgstr ""
 
 #, c-format
 msgid "Duplicate label for value %g for variable %s."
 msgstr ""
 
-#: src/pfm-read.c:961
+#: src/pfm-read.c:925
 #, c-format
 msgid "Duplicate label for value `%.*s' for variable %s."
 msgstr ""
 
 #, c-format
 msgid "Duplicate label for value `%.*s' for variable %s."
 msgstr ""
 
-#: src/pfm-read.c:1032
+#: src/pfm-read.c:978
 msgid "End of file midway through case."
 msgstr ""
 
 msgid "End of file midway through case."
 msgstr ""
 
-#: src/pfm-read.c:1042
-msgid "reading as a portable file"
-msgstr ""
-
-#: src/pfm-write.c:71
-#, c-format
-msgid "Cannot write file %s as portable file: already opened for %s."
-msgstr ""
-
-#: src/pfm-write.c:77
-#, c-format
-msgid "%s: Opening portable-file handle %s for writing."
-msgstr ""
-
-#: src/pfm-write.c:87
+#: src/pfm-write.c:92
 #, c-format
 msgid ""
 "An error occurred while opening \"%s\" for writing as a portable file: %s."
 msgstr ""
 
 #, c-format
 msgid ""
 "An error occurred while opening \"%s\" for writing as a portable file: %s."
 msgstr ""
 
-#: src/pfm-write.c:124
-msgid "Wrote portable-file header successfully."
-msgstr ""
-
-#: src/pfm-write.c:129
-msgid "Error writing portable-file header."
-msgstr ""
-
-#: src/pfm-write.c:170
+#: src/pfm-write.c:154
 #, c-format
 msgid "%s: Writing portable file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Writing portable file: %s."
 msgstr ""
 
-#: src/pfm-write.c:514
-msgid "writing as a portable file"
-msgstr ""
-
 #: src/postscript.c:323
 #, c-format
 msgid "PostScript driver initializing as `%s'..."
 #: src/postscript.c:323
 #, c-format
 msgid "PostScript driver initializing as `%s'..."
@@ -3098,80 +3013,80 @@ msgstr ""
 msgid "PostScript driver: Cannot find encoding `%s' for PostScript font `%s'."
 msgstr ""
 
 msgid "PostScript driver: Cannot find encoding `%s' for PostScript font `%s'."
 msgstr ""
 
-#: src/print.c:180
+#: src/print.c:179
 msgid "expecting a valid subcommand"
 msgstr ""
 
 msgid "expecting a valid subcommand"
 msgstr ""
 
-#: src/print.c:359 src/print.c:376
+#: src/print.c:365 src/print.c:382
 #, c-format
 msgid "%g is not a valid column location."
 msgstr ""
 
 #, c-format
 msgid "%g is not a valid column location."
 msgstr ""
 
-#: src/print.c:370
+#: src/print.c:376
 #, c-format
 msgid "Column location expected following `%d-'."
 msgstr ""
 
 #, c-format
 msgid "Column location expected following `%d-'."
 msgstr ""
 
-#: src/print.c:381
+#: src/print.c:387
 #, c-format
 msgid ""
 "%d-%ld is not a valid column range.  The second column must be greater than "
 "or equal to the first."
 msgstr ""
 
 #, c-format
 msgid ""
 "%d-%ld is not a valid column range.  The second column must be greater than "
 "or equal to the first."
 msgstr ""
 
-#: src/print.c:487
+#: src/print.c:493
 #, c-format
 msgid ""
 "%s is not of the same type as %s.  To specify variables of different types "
 "in the same variable list, use a FORTRAN-like format specifier."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s is not of the same type as %s.  To specify variables of different types "
 "in the same variable list, use a FORTRAN-like format specifier."
 msgstr ""
 
-#: src/print.c:517
+#: src/print.c:523
 msgid ""
 "The ending column for a field must not be less than the starting column."
 msgstr ""
 
 msgid ""
 "The ending column for a field must not be less than the starting column."
 msgstr ""
 
-#: src/print.c:600
+#: src/print.c:606
 #, c-format
 msgid "%s variables cannot be displayed with format %s."
 msgstr ""
 
 #, c-format
 msgid "%s variables cannot be displayed with format %s."
 msgstr ""
 
-#: src/print.c:678
+#: src/print.c:684
 msgid ""
 "The number of format specifications exceeds the number of variable names "
 "given."
 msgstr ""
 
 msgid ""
 "The number of format specifications exceeds the number of variable names "
 "given."
 msgstr ""
 
-#: src/print.c:687
+#: src/print.c:693
 #, c-format
 msgid "Display format %s may not be used with a %s variable."
 msgstr ""
 
 #, c-format
 msgid "Display format %s may not be used with a %s variable."
 msgstr ""
 
-#: src/print.c:835
+#: src/print.c:841
 #, c-format
 msgid "Writing %d record(s) to file %s."
 msgstr ""
 
 #, c-format
 msgid "Writing %d record(s) to file %s."
 msgstr ""
 
-#: src/print.c:838
+#: src/print.c:844
 #, c-format
 msgid "Writing %d record(s) to the listing file."
 msgstr ""
 
 #, c-format
 msgid "Writing %d record(s) to the listing file."
 msgstr ""
 
-#: src/print.c:1080
+#: src/print.c:1092
 #, c-format
 msgid ""
 "The expression on PRINT SPACE evaluated to %d.  It's not possible to PRINT "
 "SPACE a negative number of lines."
 msgstr ""
 
 #, c-format
 msgid ""
 "The expression on PRINT SPACE evaluated to %d.  It's not possible to PRINT "
 "SPACE a negative number of lines."
 msgstr ""
 
-#: src/recode.c:282
+#: src/recode.c:283
 #, c-format
 msgid ""
 "%d variable(s) cannot be recoded into %d variable(s).  Specify the same "
 "number of variables as input and output variables."
 msgstr ""
 
 #, c-format
 msgid ""
 "%d variable(s) cannot be recoded into %d variable(s).  Specify the same "
 "number of variables as input and output variables."
 msgstr ""
 
-#: src/recode.c:296
+#: src/recode.c:297
 #, c-format
 msgid ""
 "There is no string variable named %s.  (All string variables specified on "
 #, c-format
 msgid ""
 "There is no string variable named %s.  (All string variables specified on "
@@ -3179,83 +3094,83 @@ msgid ""
 "variable.)"
 msgstr ""
 
 "variable.)"
 msgstr ""
 
-#: src/recode.c:305
+#: src/recode.c:306
 #, c-format
 msgid ""
 "Type mismatch between input and output variables.  Output variable %s is not "
 "a string variable, but all the input variables are string variables."
 msgstr ""
 
 #, c-format
 msgid ""
 "Type mismatch between input and output variables.  Output variable %s is not "
 "a string variable, but all the input variables are string variables."
 msgstr ""
 
-#: src/recode.c:324
+#: src/recode.c:325
 #, c-format
 msgid "Type mismatch after INTO: %s is not a numeric variable."
 msgstr ""
 
 #, c-format
 msgid "Type mismatch after INTO: %s is not a numeric variable."
 msgstr ""
 
-#: src/recode.c:354
+#: src/recode.c:355
 msgid ""
 "INTO must be used when the input values are numeric and output values are "
 "string."
 msgstr ""
 
 msgid ""
 "INTO must be used when the input values are numeric and output values are "
 "string."
 msgstr ""
 
-#: src/recode.c:362
+#: src/recode.c:363
 msgid ""
 "INTO must be used when the input values are string and output values are "
 "numeric."
 msgstr ""
 
 msgid ""
 "INTO must be used when the input values are string and output values are "
 "numeric."
 msgstr ""
 
-#: src/recode.c:485
+#: src/recode.c:486
 msgid "expecting output value"
 msgstr ""
 
 msgid "expecting output value"
 msgstr ""
 
-#: src/recode.c:499
+#: src/recode.c:500
 msgid ""
 "Inconsistent output types.  The output values must be all numeric or all "
 "string."
 msgstr ""
 
 msgid ""
 "Inconsistent output types.  The output values must be all numeric or all "
 "string."
 msgstr ""
 
-#: src/recode.c:550
+#: src/recode.c:551
 msgid "following LO THRU"
 msgstr ""
 
 msgid "following LO THRU"
 msgstr ""
 
-#: src/recode.c:566 src/recode.c:595
+#: src/recode.c:567 src/recode.c:596
 msgid "in source value"
 msgstr ""
 
 msgid "in source value"
 msgstr ""
 
-#: src/recode.c:608
+#: src/recode.c:609
 msgid ""
 "Keyword CONVERT may only be used with string input values and numeric output "
 "values."
 msgstr ""
 
 msgid ""
 "Keyword CONVERT may only be used with string input values and numeric output "
 "values."
 msgstr ""
 
-#: src/rename-vars.c:47
+#: src/rename-vars.c:48
 msgid ""
 "RENAME VARS may not be used after TEMPORARY.  Temporary transformations will "
 "be made permanent."
 msgstr ""
 
 msgid ""
 "RENAME VARS may not be used after TEMPORARY.  Temporary transformations will "
 "be made permanent."
 msgstr ""
 
-#: src/rename-vars.c:59
+#: src/rename-vars.c:60
 msgid "`(' expected."
 msgstr ""
 
 msgid "`(' expected."
 msgstr ""
 
-#: src/rename-vars.c:67
+#: src/rename-vars.c:68
 msgid "`=' expected between lists of new and old variable names."
 msgstr ""
 
 msgid "`=' expected between lists of new and old variable names."
 msgstr ""
 
-#: src/rename-vars.c:85
+#: src/rename-vars.c:86
 msgid "`)' expected after variable names."
 msgstr ""
 
 msgid "`)' expected after variable names."
 msgstr ""
 
-#: src/rename-vars.c:95
+#: src/rename-vars.c:96
 #, c-format
 msgid "Renaming would duplicate variable name %s."
 msgstr ""
 
 #, c-format
 msgid "Renaming would duplicate variable name %s."
 msgstr ""
 
-#: src/repeat.c:150
+#: src/repeat.c:151
 #, c-format
 msgid "Identifier %s is given twice."
 msgstr ""
 
 #, c-format
 msgid "Identifier %s is given twice."
 msgstr ""
 
-#: src/repeat.c:193
+#: src/repeat.c:194
 #, c-format
 msgid ""
 "There must be the same number of substitutions for each dummy variable "
 #, c-format
 msgid ""
 "There must be the same number of substitutions for each dummy variable "
@@ -3263,11 +3178,11 @@ msgid ""
 "s as well, but %d were specified."
 msgstr ""
 
 "s as well, but %d were specified."
 msgstr ""
 
-#: src/repeat.c:298
+#: src/repeat.c:299
 msgid "No commands in scope."
 msgstr ""
 
 msgid "No commands in scope."
 msgstr ""
 
-#: src/repeat.c:486
+#: src/repeat.c:487
 msgid "No matching DO REPEAT."
 msgstr ""
 
 msgid "No matching DO REPEAT."
 msgstr ""
 
@@ -3280,129 +3195,111 @@ msgstr ""
 msgid "Cannot sample %d observations from a population of %d."
 msgstr ""
 
 msgid "Cannot sample %d observations from a population of %d."
 msgstr ""
 
-#: src/sel-if.c:99
+#: src/sel-if.c:100
 msgid "The filter variable must be numeric."
 msgstr ""
 
 msgid "The filter variable must be numeric."
 msgstr ""
 
-#: src/sel-if.c:105
+#: src/sel-if.c:106
 msgid "The filter variable may not be scratch."
 msgstr ""
 
 msgid "The filter variable may not be scratch."
 msgstr ""
 
-#: src/sel-if.c:136
+#: src/sel-if.c:137
 msgid "Only last instance of this command is in effect."
 msgstr ""
 
 msgid "Only last instance of this command is in effect."
 msgstr ""
 
-#: src/sfm-read.c:147
+#: src/sfm-read.c:140
 msgid "corrupt system file: "
 msgstr ""
 
 msgid "corrupt system file: "
 msgstr ""
 
-#: src/sfm-read.c:163 src/sfm-write.c:743
+#: src/sfm-read.c:157 src/sfm-write.c:808
 #, c-format
 msgid "%s: Closing system file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Closing system file: %s."
 msgstr ""
 
-#: src/sfm-read.c:237
-#, c-format
-msgid "Cannot read file %s as system file: already opened for %s."
-msgstr ""
-
-#: src/sfm-read.c:242
-#, c-format
-msgid "%s: Opening system-file handle %s for reading."
-msgstr ""
-
-#: src/sfm-read.c:250
+#: src/sfm-read.c:240
 #, c-format
 msgid ""
 "An error occurred while opening \"%s\" for reading as a system file: %s."
 msgstr ""
 
 #, c-format
 msgid ""
 "An error occurred while opening \"%s\" for reading as a system file: %s."
 msgstr ""
 
-#: src/sfm-read.c:285
+#: src/sfm-read.c:258
 #, c-format
 msgid ""
 "%s: Weighting variable may not be a continuation of a long string variable."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s: Weighting variable may not be a continuation of a long string variable."
 msgstr ""
 
-#: src/sfm-read.c:288
+#: src/sfm-read.c:261
 #, c-format
 msgid "%s: Weighting variable may not be a string variable."
 msgstr ""
 
 #, c-format
 msgid "%s: Weighting variable may not be a string variable."
 msgstr ""
 
-#: src/sfm-read.c:313
+#: src/sfm-read.c:286
 #, c-format
 msgid ""
 "%s: Orphaned variable index record (type 4).  Type 4 records must always "
 "immediately follow type 3 records."
 msgstr ""
 
 #, 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:362
+#: src/sfm-read.c:335
 #, c-format
 msgid "%s: Unrecognized record type 7, subtype %d encountered in system file."
 msgstr ""
 
 #, c-format
 msgid "%s: Unrecognized record type 7, subtype %d encountered in system file."
 msgstr ""
 
-#: src/sfm-read.c:387
+#: src/sfm-read.c:360
 #, c-format
 msgid "%s: Unrecognized record type %d."
 msgstr ""
 
 #, c-format
 msgid "%s: Unrecognized record type %d."
 msgstr ""
 
-#: src/sfm-read.c:394
-msgid "Read system-file dictionary successfully."
-msgstr ""
-
-#: src/sfm-read.c:401
-msgid "Error reading system-file header."
-msgstr ""
-
-#: src/sfm-read.c:425
+#: src/sfm-read.c:392
 #, c-format
 msgid ""
 "%s: Bad size (%d) or count (%d) field on record type 7, subtype 3.\tExpected "
 "size %d, count 8."
 msgstr ""
 
 #, 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:437
+#: src/sfm-read.c:403
 #, c-format
 msgid ""
 "%s: Floating-point representation in system file is not IEEE-754.  PSPP "
 "cannot convert between floating-point formats."
 msgstr ""
 
 #, 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:453
+#: src/sfm-read.c:419
 #, c-format
 msgid ""
 "%s: File-indicated endianness (%s) does not match endianness intuited from "
 "file header (%s)."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s: File-indicated endianness (%s) does not match endianness intuited from "
 "file header (%s)."
 msgstr ""
 
-#: src/sfm-read.c:456 src/sfm-read.c:457
+#: src/sfm-read.c:422 src/sfm-read.c:423
 msgid "big-endian"
 msgstr ""
 
 msgid "big-endian"
 msgstr ""
 
-#: src/sfm-read.c:456 src/sfm-read.c:457
+#: src/sfm-read.c:422 src/sfm-read.c:423
 msgid "little-endian"
 msgstr ""
 
 msgid "little-endian"
 msgstr ""
 
-#: src/sfm-read.c:458
+#: src/sfm-read.c:424
 msgid "unknown"
 msgstr ""
 
 msgid "unknown"
 msgstr ""
 
-#: src/sfm-read.c:462
+#: src/sfm-read.c:428
 #, c-format
 msgid "%s: File-indicated character representation code (%s) is not ASCII."
 msgstr ""
 
 #, c-format
 msgid "%s: File-indicated character representation code (%s) is not ASCII."
 msgstr ""
 
-#: src/sfm-read.c:466
+#: src/sfm-read.c:432
 msgid "DEC Kanji"
 msgstr ""
 
 msgid "DEC Kanji"
 msgstr ""
 
-#: src/sfm-read.c:485
+#: src/sfm-read.c:448
 #, c-format
 msgid ""
 "%s: Bad size (%d) or count (%d) field on record type 7, subtype 4.\tExpected "
 "size %d, count 8."
 msgstr ""
 
 #, 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:500
+#: src/sfm-read.c:463
 #, c-format
 msgid ""
 "%s: File-indicated value is different from internal value for at least one "
 #, c-format
 msgid ""
 "%s: File-indicated value is different from internal value for at least one "
@@ -3410,263 +3307,236 @@ msgid ""
 "%g; LOWEST: %g, %g."
 msgstr ""
 
 "%g; LOWEST: %g, %g."
 msgstr ""
 
-#: src/sfm-read.c:531
+#: src/sfm-read.c:490
 #, c-format
 msgid ""
 "%s: Bad magic.  Proper system files begin with the four characters `$FL2'. "
 "This file will not be read."
 msgstr ""
 
 #, 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:574
+#: src/sfm-read.c:532
 #, c-format
 msgid ""
 "%s: File layout code has unexpected value %d.  Value should be 2, in big-"
 "endian or little-endian format."
 msgstr ""
 
 #, 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:590
+#: src/sfm-read.c:548
 #, c-format
 msgid "%s: Number of elements per case (%d) is not between 1 and %d."
 msgstr ""
 
 #, c-format
 msgid "%s: Number of elements per case (%d) is not between 1 and %d."
 msgstr ""
 
-#: src/sfm-read.c:599
+#: src/sfm-read.c:557
 #, c-format
 msgid ""
 "%s: Index of weighting variable (%d) is not between 0 and number of elements "
 "per case (%d)."
 msgstr ""
 
 #, 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:605
+#: src/sfm-read.c:564
 #, c-format
 msgid "%s: Number of cases in file (%ld) is not between -1 and %d."
 msgstr ""
 
 #, c-format
 msgid "%s: Number of cases in file (%ld) is not between -1 and %d."
 msgstr ""
 
-#: src/sfm-read.c:610
+#: src/sfm-read.c:569
 #, c-format
 msgid "%s: Compression bias (%g) is not the usual value of 100."
 msgstr ""
 
 #, c-format
 msgid "%s: Compression bias (%g) is not the usual value of 100."
 msgstr ""
 
-#: src/sfm-read.c:704
+#: src/sfm-read.c:662
 #, c-format
 msgid "%s: position %d: Bad record type (%d); the expected value was 2."
 msgstr ""
 
 #, c-format
 msgid "%s: position %d: Bad record type (%d); the expected value was 2."
 msgstr ""
 
-#: src/sfm-read.c:714
+#: src/sfm-read.c:672
 #, c-format
 msgid ""
 "%s: position %d: String variable does not have proper number of continuation "
 "records."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s: position %d: String variable does not have proper number of continuation "
 "records."
 msgstr ""
 
-#: src/sfm-read.c:723
+#: src/sfm-read.c:681
 #, c-format
 msgid "%s: position %d: Superfluous long string continuation record."
 msgstr ""
 
 #, c-format
 msgid "%s: position %d: Superfluous long string continuation record."
 msgstr ""
 
-#: src/sfm-read.c:729
+#: src/sfm-read.c:687
 #, c-format
 msgid "%s: position %d: Bad variable type code %d."
 msgstr ""
 
 #, c-format
 msgid "%s: position %d: Bad variable type code %d."
 msgstr ""
 
-#: src/sfm-read.c:732
+#: src/sfm-read.c:690
 #, c-format
 msgid "%s: position %d: Variable label indicator field is not 0 or 1."
 msgstr ""
 
 #, c-format
 msgid "%s: position %d: Variable label indicator field is not 0 or 1."
 msgstr ""
 
-#: src/sfm-read.c:736
+#: src/sfm-read.c:694
 #, c-format
 msgid ""
 "%s: position %d: Missing value indicator field is not -3, -2, 0, 1, 2, or 3."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s: position %d: Missing value indicator field is not -3, -2, 0, 1, 2, or 3."
 msgstr ""
 
-#: src/sfm-read.c:742
+#: src/sfm-read.c:700
 #, c-format
 msgid "%s: position %d: Variable name begins with invalid character."
 msgstr ""
 
 #, c-format
 msgid "%s: position %d: Variable name begins with invalid character."
 msgstr ""
 
-#: src/sfm-read.c:746
+#: src/sfm-read.c:704
 #, c-format
 msgid "%s: position %d: Variable name begins with lowercase letter %c."
 msgstr ""
 
 #, c-format
 msgid "%s: position %d: Variable name begins with lowercase letter %c."
 msgstr ""
 
-#: src/sfm-read.c:750
+#: src/sfm-read.c:708
 #, c-format
 msgid ""
 "%s: position %d: Variable name begins with octothorpe (`#').  Scratch "
 "variables should not appear in system files."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s: position %d: Variable name begins with octothorpe (`#').  Scratch "
 "variables should not appear in system files."
 msgstr ""
 
-#: src/sfm-read.c:765
+#: src/sfm-read.c:723
 #, c-format
 msgid "%s: position %d: Variable name character %d is lowercase letter %c."
 msgstr ""
 
 #, c-format
 msgid "%s: position %d: Variable name character %d is lowercase letter %c."
 msgstr ""
 
-#: src/sfm-read.c:774
+#: src/sfm-read.c:732
 #, c-format
 msgid ""
 "%s: position %d: character `\\%03o' (%c) is not valid in a variable name."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s: position %d: character `\\%03o' (%c) is not valid in a variable name."
 msgstr ""
 
-#: src/sfm-read.c:783
+#: src/sfm-read.c:741
 #, c-format
 msgid "%s: Duplicate variable name `%s' within system file."
 msgstr ""
 
 #, c-format
 msgid "%s: Duplicate variable name `%s' within system file."
 msgstr ""
 
-#: src/sfm-read.c:808
+#: src/sfm-read.c:762
 #, c-format
 msgid "%s: Variable %s indicates variable label of invalid length %d."
 msgstr ""
 
 #, c-format
 msgid "%s: Variable %s indicates variable label of invalid length %d."
 msgstr ""
 
-#: src/sfm-read.c:825
+#: src/sfm-read.c:779
 #, c-format
 msgid "%s: Long string variable %s may not have missing values."
 msgstr ""
 
 #, c-format
 msgid "%s: Long string variable %s may not have missing values."
 msgstr ""
 
-#: src/sfm-read.c:850
+#: src/sfm-read.c:804
 #, c-format
 msgid ""
 "%s: String variable %s may not have missing values specified as a range."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s: String variable %s may not have missing values specified as a range."
 msgstr ""
 
-#: src/sfm-read.c:888
+#: src/sfm-read.c:852
 #, c-format
 msgid "%s: Long string continuation records omitted at end of dictionary."
 msgstr ""
 
 #, c-format
 msgid "%s: Long string continuation records omitted at end of dictionary."
 msgstr ""
 
-#: src/sfm-read.c:892
+#: src/sfm-read.c:856
 #, c-format
 msgid ""
 "%s: System file header indicates %d variable positions but %d were read from "
 "file."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s: System file header indicates %d variable positions but %d were read from "
 "file."
 msgstr ""
 
-#: src/sfm-read.c:923
+#: src/sfm-read.c:884
 #, c-format
 msgid "%s: %s variable %s has %s format specifier %s."
 msgstr ""
 
 #, c-format
 msgid "%s: %s variable %s has %s format specifier %s."
 msgstr ""
 
-#: src/sfm-read.c:1003
+#: src/sfm-read.c:963
 #, c-format
 msgid ""
 "%s: Variable index record (type 4) does not immediately follow value label "
 "record (type 3) as it should."
 msgstr ""
 
 #, 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:1014
+#: src/sfm-read.c:974
 #, c-format
 msgid ""
 "%s: Number of variables associated with a value label (%d) is not between 1 "
 "and the number of variables (%d)."
 msgstr ""
 
 #, 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:1030
+#: src/sfm-read.c:990
 #, c-format
 msgid ""
 "%s: Variable index associated with value label (%d) is not between 1 and the "
 "number of values (%d)."
 msgstr ""
 
 #, 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:1037
+#: src/sfm-read.c:997
 #, 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 ""
 
 #, 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:1042
+#: src/sfm-read.c:1002
 #, c-format
 msgid "%s: Value labels are not allowed on long string variables (%s)."
 msgstr ""
 
 #, c-format
 msgid "%s: Value labels are not allowed on long string variables (%s)."
 msgstr ""
 
-#: src/sfm-read.c:1053
+#: src/sfm-read.c:1013
 #, 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 ""
 
 #, 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:1094
+#: src/sfm-read.c:1054
 #, c-format
 msgid "%s: File contains duplicate label for value %g for variable %s."
 msgstr ""
 
 #, c-format
 msgid "%s: File contains duplicate label for value %g for variable %s."
 msgstr ""
 
-#: src/sfm-read.c:1098
+#: src/sfm-read.c:1058
 #, c-format
 msgid "%s: File contains duplicate label for value `%.*s' for variable %s."
 msgstr ""
 
 #, c-format
 msgid "%s: File contains duplicate label for value `%.*s' for variable %s."
 msgstr ""
 
-#: src/sfm-read.c:1135 src/sfm-read.c:1338
+#: src/sfm-read.c:1093 src/sfm-read.c:1357
 #, c-format
 msgid "%s: Reading system file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Reading system file: %s."
 msgstr ""
 
-#: src/sfm-read.c:1138 src/sfm-read.c:1245 src/sfm-read.c:1287
+#: src/sfm-read.c:1096 src/sfm-read.c:1198 src/sfm-read.c:1240
 #, c-format
 msgid "%s: Unexpected end of file."
 msgstr ""
 
 #, c-format
 msgid "%s: Unexpected end of file."
 msgstr ""
 
-#: src/sfm-read.c:1157
+#: src/sfm-read.c:1113
 #, c-format
 msgid "%s: System file contains multiple type 6 (document) records."
 msgstr ""
 
 #, c-format
 msgid "%s: System file contains multiple type 6 (document) records."
 msgstr ""
 
-#: src/sfm-read.c:1163
+#: src/sfm-read.c:1119
 #, c-format
 msgid "%s: Number of document lines (%ld) must be greater than 0."
 msgstr ""
 
 #, c-format
 msgid "%s: Number of document lines (%ld) must be greater than 0."
 msgstr ""
 
-#: src/sfm-read.c:1196
+#: src/sfm-read.c:1151
 #, c-format
 msgid "%s: Error reading file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Error reading file: %s."
 msgstr ""
 
-#: src/sfm-read.c:1235
+#: src/sfm-read.c:1188
 #, c-format
 msgid "%s: Compressed data is corrupted.  Data ends in partial case."
 msgstr ""
 
 #, c-format
 msgid "%s: Compressed data is corrupted.  Data ends in partial case."
 msgstr ""
 
-#: src/sfm-read.c:1341
+#: src/sfm-read.c:1360
 #, c-format
 msgid "%s: Partial record at end of system file."
 msgstr ""
 
 #, c-format
 msgid "%s: Partial record at end of system file."
 msgstr ""
 
-#: src/sfm-read.c:1380
-msgid "reading as a system file"
-msgstr ""
-
-#: src/sfm-write.c:95
-#, c-format
-msgid "Cannot write file %s as system file: already opened for %s."
-msgstr ""
-
-#: src/sfm-write.c:101
-#, c-format
-msgid "%s: Opening system-file handle %s for writing."
-msgstr ""
-
-#: src/sfm-write.c:111
+#: src/sfm-write.c:143
 #, c-format
 #, c-format
-msgid ""
-"An error occurred while opening \"%s\" for writing as a system file: %s."
-msgstr ""
-
-#: src/sfm-write.c:165
-msgid "Wrote system-file header successfully."
+msgid "Error opening \"%s\" for writing as a system file: %s."
 msgstr ""
 
 msgstr ""
 
-#: src/sfm-write.c:170
-msgid "Error writing system-file header."
-msgstr ""
-
-#: src/sfm-write.c:606
+#: src/sfm-write.c:628
 #, c-format
 msgid "%s: Writing system file: %s."
 msgstr ""
 
 #, c-format
 msgid "%s: Writing system file: %s."
 msgstr ""
 
-#: src/sfm-write.c:754
-msgid "writing as a system file"
-msgstr ""
-
 #: src/sort.c:197
 msgid "`A' or `D' expected inside parentheses."
 msgstr ""
 #: src/sort.c:197
 msgid "`A' or `D' expected inside parentheses."
 msgstr ""
@@ -3682,141 +3552,141 @@ msgid ""
 "each.  (PSPP workspace is currently restricted to a maximum of %d KB.)"
 msgstr ""
 
 "each.  (PSPP workspace is currently restricted to a maximum of %d KB.)"
 msgstr ""
 
-#: src/sysfile-info.c:94
+#: src/sysfile-info.c:96
 msgid "File:"
 msgstr ""
 
 msgid "File:"
 msgstr ""
 
-#: src/sysfile-info.c:96
+#: src/sysfile-info.c:98
 msgid "Label:"
 msgstr ""
 
 msgid "Label:"
 msgstr ""
 
-#: src/sysfile-info.c:100
+#: src/sysfile-info.c:102
 msgid "No label."
 msgstr ""
 
 msgid "No label."
 msgstr ""
 
-#: src/sysfile-info.c:103
+#: src/sysfile-info.c:105
 msgid "Created:"
 msgstr ""
 
 msgid "Created:"
 msgstr ""
 
-#: src/sysfile-info.c:106
+#: src/sysfile-info.c:108
 msgid "Endian:"
 msgstr ""
 
 msgid "Endian:"
 msgstr ""
 
-#: src/sysfile-info.c:107
+#: src/sysfile-info.c:109
 msgid "Big."
 msgstr ""
 
 msgid "Big."
 msgstr ""
 
-#: src/sysfile-info.c:107
+#: src/sysfile-info.c:109
 msgid "Little."
 msgstr ""
 
 msgid "Little."
 msgstr ""
 
-#: src/sysfile-info.c:108
+#: src/sysfile-info.c:110
 msgid "Variables:"
 msgstr ""
 
 msgid "Variables:"
 msgstr ""
 
-#: src/sysfile-info.c:111
+#: src/sysfile-info.c:113
 msgid "Cases:"
 msgstr ""
 
 msgid "Cases:"
 msgstr ""
 
-#: src/sysfile-info.c:114
+#: src/sysfile-info.c:116
 msgid "Type:"
 msgstr ""
 
 msgid "Type:"
 msgstr ""
 
-#: src/sysfile-info.c:115
+#: src/sysfile-info.c:117
 msgid "System File."
 msgstr ""
 
 msgid "System File."
 msgstr ""
 
-#: src/sysfile-info.c:116
+#: src/sysfile-info.c:118
 msgid "Weight:"
 msgstr ""
 
 msgid "Weight:"
 msgstr ""
 
-#: src/sysfile-info.c:120
+#: src/sysfile-info.c:122
 msgid "Not weighted."
 msgstr ""
 
 msgid "Not weighted."
 msgstr ""
 
-#: src/sysfile-info.c:122
+#: src/sysfile-info.c:124
 msgid "Mode:"
 msgstr ""
 
 msgid "Mode:"
 msgstr ""
 
-#: src/sysfile-info.c:124
+#: src/sysfile-info.c:126
 #, c-format
 msgid "Compression %s."
 msgstr ""
 
 #, c-format
 msgid "Compression %s."
 msgstr ""
 
-#: src/sysfile-info.c:124
+#: src/sysfile-info.c:126
 msgid "on"
 msgstr ""
 
 msgid "on"
 msgstr ""
 
-#: src/sysfile-info.c:124
+#: src/sysfile-info.c:126
 msgid "off"
 msgstr ""
 
 msgid "off"
 msgstr ""
 
-#: src/sysfile-info.c:133 src/sysfile-info.c:370
+#: src/sysfile-info.c:135 src/sysfile-info.c:372
 msgid "Description"
 msgstr ""
 
 msgid "Description"
 msgstr ""
 
-#: src/sysfile-info.c:134 src/sysfile-info.c:368
+#: src/sysfile-info.c:136 src/sysfile-info.c:370
 msgid "Position"
 msgstr ""
 
 msgid "Position"
 msgstr ""
 
-#: src/sysfile-info.c:191
+#: src/sysfile-info.c:193
 msgid "The active file does not have a file label."
 msgstr ""
 
 msgid "The active file does not have a file label."
 msgstr ""
 
-#: src/sysfile-info.c:194
+#: src/sysfile-info.c:196
 msgid "File label:"
 msgstr ""
 
 msgid "File label:"
 msgstr ""
 
-#: src/sysfile-info.c:256
+#: src/sysfile-info.c:258
 msgid "No variables to display."
 msgstr ""
 
 msgid "No variables to display."
 msgstr ""
 
-#: src/sysfile-info.c:275
+#: src/sysfile-info.c:277
 msgid "Macros not supported."
 msgstr ""
 
 msgid "Macros not supported."
 msgstr ""
 
-#: src/sysfile-info.c:285
+#: src/sysfile-info.c:287
 msgid "The active file dictionary does not contain any documents."
 msgstr ""
 
 msgid "The active file dictionary does not contain any documents."
 msgstr ""
 
-#: src/sysfile-info.c:294
+#: src/sysfile-info.c:296
 msgid "Documents in the active file:"
 msgstr ""
 
 msgid "Documents in the active file:"
 msgstr ""
 
-#: src/sysfile-info.c:372 src/sysfile-info.c:530 src/vfm.c:876
+#: src/sysfile-info.c:374 src/sysfile-info.c:532 src/vfm.c:877
 msgid "Label"
 msgstr ""
 
 msgid "Label"
 msgstr ""
 
-#: src/sysfile-info.c:444
+#: src/sysfile-info.c:446
 #, c-format
 msgid "Format: %s"
 msgstr ""
 
 #, c-format
 msgid "Format: %s"
 msgstr ""
 
-#: src/sysfile-info.c:451
+#: src/sysfile-info.c:453
 #, c-format
 msgid "Print Format: %s"
 msgstr ""
 
 #, c-format
 msgid "Print Format: %s"
 msgstr ""
 
-#: src/sysfile-info.c:454
+#: src/sysfile-info.c:456
 #, c-format
 msgid "Write Format: %s"
 msgstr ""
 
 #, c-format
 msgid "Write Format: %s"
 msgstr ""
 
-#: src/sysfile-info.c:462
+#: src/sysfile-info.c:464
 msgid "Missing Values: "
 msgstr ""
 
 msgid "Missing Values: "
 msgstr ""
 
-#: src/sysfile-info.c:529 src/vfm.c:875 src/crosstabs.q:1068
-#: src/crosstabs.q:1095 src/crosstabs.q:1115 src/crosstabs.q:1137
-#: src/examine.q:1125 src/frequencies.q:1056 src/frequencies.q:1174
+#: src/sysfile-info.c:531 src/vfm.c:876 src/crosstabs.q:1099
+#: src/crosstabs.q:1126 src/crosstabs.q:1146 src/crosstabs.q:1168
+#: src/examine.q:1179 src/frequencies.q:1083 src/frequencies.q:1204
 msgid "Value"
 msgstr ""
 
 msgid "Value"
 msgstr ""
 
-#: src/sysfile-info.c:586
+#: src/sysfile-info.c:588
 msgid "No vectors defined."
 msgstr ""
 
 msgid "No vectors defined."
 msgstr ""
 
-#: src/sysfile-info.c:601
+#: src/sysfile-info.c:603
 msgid "Vector"
 msgstr ""
 
 msgid "Vector"
 msgstr ""
 
@@ -3831,36 +3701,36 @@ msgid ""
 "bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
 msgstr ""
 
 "bad box: (%d+%d=%d,%d+%d=%d)-(%d+%d=%d,%d+%d=%d) in table size (%d,%d)\n"
 msgstr ""
 
-#: src/temporary.c:45
+#: src/temporary.c:46
 msgid "This command is not valid inside DO IF or LOOP."
 msgstr ""
 
 msgid "This command is not valid inside DO IF or LOOP."
 msgstr ""
 
-#: src/temporary.c:52
+#: src/temporary.c:53
 msgid ""
 "This command may only appear once between procedures and procedure-like "
 "commands."
 msgstr ""
 
 msgid ""
 "This command may only appear once between procedures and procedure-like "
 "commands."
 msgstr ""
 
-#: src/title.c:55
+#: src/title.c:56
 #, c-format
 msgid "%s before: %s\n"
 msgstr ""
 
 #, c-format
 msgid "%s before: %s\n"
 msgstr ""
 
-#: src/title.c:55
+#: src/title.c:56
 msgid "<none>"
 msgstr ""
 
 msgid "<none>"
 msgstr ""
 
-#: src/title.c:67
+#: src/title.c:68
 #, c-format
 msgid "%s: `.' expected after string."
 msgstr ""
 
 #, c-format
 msgid "%s: `.' expected after string."
 msgstr ""
 
-#: src/title.c:83
+#: src/title.c:84
 #, c-format
 msgid "%s after: %s\n"
 msgstr ""
 
 #, c-format
 msgid "%s after: %s\n"
 msgstr ""
 
-#: src/title.c:139
+#: src/title.c:140
 #, c-format
 msgid "Document entered %s %02d:%02d:%02d by %s (%s):"
 msgstr ""
 #, c-format
 msgid "Document entered %s %02d:%02d:%02d by %s (%s):"
 msgstr ""
@@ -3889,29 +3759,29 @@ msgstr ""
 msgid "Truncating variable label to 255 characters."
 msgstr ""
 
 msgid "Truncating variable label to 255 characters."
 msgstr ""
 
-#: src/vars-prs.c:48
+#: src/vars-prs.c:49
 #, c-format
 msgid "%s is not a variable name."
 msgstr ""
 
 #, c-format
 msgid "%s is not a variable name."
 msgstr ""
 
-#: src/vars-prs.c:100
+#: src/vars-prs.c:101
 msgid "ordinary"
 msgstr ""
 
 msgid "ordinary"
 msgstr ""
 
-#: src/vars-prs.c:102
+#: src/vars-prs.c:103
 msgid "system"
 msgstr ""
 
 msgid "system"
 msgstr ""
 
-#: src/vars-prs.c:104
+#: src/vars-prs.c:105
 msgid "scratch"
 msgstr ""
 
 msgid "scratch"
 msgstr ""
 
-#: src/vars-prs.c:209
+#: src/vars-prs.c:210
 #, c-format
 msgid "%s TO %s is not valid syntax since %s precedes %s in the dictionary."
 msgstr ""
 
 #, c-format
 msgid "%s TO %s is not valid syntax since %s precedes %s in the dictionary."
 msgstr ""
 
-#: src/vars-prs.c:219
+#: src/vars-prs.c:220
 #, c-format
 msgid ""
 "When using the TO keyword to specify several variables, both variables must "
 #, c-format
 msgid ""
 "When using the TO keyword to specify several variables, both variables must "
@@ -3919,700 +3789,706 @@ msgid ""
 "system variables.  %s is a %s variable, whereas %s is %s."
 msgstr ""
 
 "system variables.  %s is a %s variable, whereas %s is %s."
 msgstr ""
 
-#: src/vars-prs.c:237
+#: src/vars-prs.c:238
 #, c-format
 msgid "Scratch variables (such as %s) are not allowed here."
 msgstr ""
 
 #, c-format
 msgid "Scratch variables (such as %s) are not allowed here."
 msgstr ""
 
-#: src/vars-prs.c:260
+#: src/vars-prs.c:261
 #, c-format
 msgid ""
 "%s is not a numeric variable.  It will not be included in the variable list."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s is not a numeric variable.  It will not be included in the variable list."
 msgstr ""
 
-#: src/vars-prs.c:263
+#: src/vars-prs.c:264
 #, c-format
 msgid ""
 "%s is not a string variable.  It will not be included in the variable list."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s is not a string variable.  It will not be included in the variable list."
 msgstr ""
 
-#: src/vars-prs.c:267
+#: src/vars-prs.c:268
 #, c-format
 msgid ""
 "%s and %s are not the same type.  All variables in this variable list must "
 "be of the same type.  %s will be omitted from list."
 msgstr ""
 
 #, c-format
 msgid ""
 "%s and %s are not the same type.  All variables in this variable list must "
 "be of the same type.  %s will be omitted from list."
 msgstr ""
 
-#: src/vars-prs.c:272
+#: src/vars-prs.c:273
 #, c-format
 msgid "Variable %s appears twice in variable list."
 msgstr ""
 
 #, c-format
 msgid "Variable %s appears twice in variable list."
 msgstr ""
 
-#: src/vars-prs.c:352
+#: src/vars-prs.c:353
 msgid "incorrect use of TO convention"
 msgstr ""
 
 msgid "incorrect use of TO convention"
 msgstr ""
 
-#: src/vars-prs.c:399
+#: src/vars-prs.c:400
 msgid "Scratch variables not allowed here."
 msgstr ""
 
 msgid "Scratch variables not allowed here."
 msgstr ""
 
-#: src/vars-prs.c:421
+#: src/vars-prs.c:422
 msgid "Prefixes don't match in use of TO convention."
 msgstr ""
 
 msgid "Prefixes don't match in use of TO convention."
 msgstr ""
 
-#: src/vars-prs.c:426
+#: src/vars-prs.c:427
 msgid "Bad bounds in use of TO convention."
 msgstr ""
 
 msgid "Bad bounds in use of TO convention."
 msgstr ""
 
-#: src/vector.c:66
+#: src/vector.c:67
 #, c-format
 msgid "Vector name %s is given twice."
 msgstr ""
 
 #, c-format
 msgid "Vector name %s is given twice."
 msgstr ""
 
-#: src/vector.c:72
+#: src/vector.c:73
 #, c-format
 msgid "There is already a vector with name %s."
 msgstr ""
 
 #, c-format
 msgid "There is already a vector with name %s."
 msgstr ""
 
-#: src/vector.c:93
+#: src/vector.c:94
 msgid ""
 "A slash must be used to separate each vector specification when using the "
 "long form.  Commands such as VECTOR A,B=Q1 TO Q20 are not supported."
 msgstr ""
 
 msgid ""
 "A slash must be used to separate each vector specification when using the "
 "long form.  Commands such as VECTOR A,B=Q1 TO Q20 are not supported."
 msgstr ""
 
-#: src/vector.c:127
+#: src/vector.c:128
 msgid "Vectors must have at least one element."
 msgstr ""
 
 msgid "Vectors must have at least one element."
 msgstr ""
 
-#: src/vector.c:141
+#: src/vector.c:142
 #, c-format
 msgid "%s%d is too long for a variable name."
 msgstr ""
 
 #, c-format
 msgid "%s%d is too long for a variable name."
 msgstr ""
 
-#: src/vector.c:179
+#: src/vector.c:180
 msgid ""
 "The syntax for this command does not match the expected syntax for either "
 "the long form or the short form of VECTOR."
 msgstr ""
 
 msgid ""
 "The syntax for this command does not match the expected syntax for either "
 "the long form or the short form of VECTOR."
 msgstr ""
 
-#: src/weight.c:52
+#: src/weight.c:53
 msgid "The weighting variable must be numeric."
 msgstr ""
 
 msgid "The weighting variable must be numeric."
 msgstr ""
 
-#: src/weight.c:57
+#: src/weight.c:58
 msgid "The weighting variable may not be scratch."
 msgstr ""
 
 msgid "The weighting variable may not be scratch."
 msgstr ""
 
-#: src/crosstabs.q:241
+#: src/crosstabs.q:261
 msgid ""
 "Missing mode REPORT not allowed in general mode.  Assuming MISSING=TABLE."
 msgstr ""
 
 msgid ""
 "Missing mode REPORT not allowed in general mode.  Assuming MISSING=TABLE."
 msgstr ""
 
-#: src/crosstabs.q:251
+#: src/crosstabs.q:271
 msgid "Write mode ALL not allowed in general mode.  Assuming WRITE=CELLS."
 msgstr ""
 
 msgid "Write mode ALL not allowed in general mode.  Assuming WRITE=CELLS."
 msgstr ""
 
-#: src/crosstabs.q:312
+#: src/crosstabs.q:332
 msgid "expecting BY"
 msgstr ""
 
 msgid "expecting BY"
 msgstr ""
 
-#: src/crosstabs.q:379
+#: src/crosstabs.q:399
 msgid "VARIABLES must be specified before TABLES."
 msgstr ""
 
 msgid "VARIABLES must be specified before TABLES."
 msgstr ""
 
-#: src/crosstabs.q:416
+#: src/crosstabs.q:436
 #, c-format
 msgid "Maximum value (%ld) less than minimum value (%ld)."
 msgstr ""
 
 #, c-format
 msgid "Maximum value (%ld) less than minimum value (%ld)."
 msgstr ""
 
-#: src/crosstabs.q:769
+#: src/crosstabs.q:800
 msgid "Summary."
 msgstr ""
 
 msgid "Summary."
 msgstr ""
 
-#: src/crosstabs.q:771 src/examine.q:784
+#: src/crosstabs.q:802 src/examine.q:837
 msgid "Cases"
 msgstr ""
 
 msgid "Cases"
 msgstr ""
 
-#: src/crosstabs.q:772 src/examine.q:722 src/frequencies.q:1054
-#: src/frequencies.q:1421
+#: src/crosstabs.q:803 src/examine.q:775 src/frequencies.q:1081
+#: src/frequencies.q:1454
 msgid "Valid"
 msgstr ""
 
 msgid "Valid"
 msgstr ""
 
-#: src/crosstabs.q:773 src/examine.q:723 src/frequencies.q:1121
-#: src/frequencies.q:1422
+#: src/crosstabs.q:804 src/examine.q:776 src/frequencies.q:1149
+#: src/frequencies.q:1455
 msgid "Missing"
 msgstr ""
 
 msgid "Missing"
 msgstr ""
 
-#: src/crosstabs.q:774 src/crosstabs.q:977 src/crosstabs.q:1690
-#: src/examine.q:724 src/frequencies.q:1130 src/oneway.q:306 src/oneway.q:483
+#: src/crosstabs.q:805 src/crosstabs.q:1008 src/crosstabs.q:1722
+#: src/examine.q:777 src/frequencies.q:1158 src/oneway.q:307 src/oneway.q:486
 msgid "Total"
 msgstr ""
 
 msgid "Total"
 msgstr ""
 
-#: src/crosstabs.q:784 src/examine.q:805 src/frequencies.q:1420
-#: src/oneway.q:393 src/t-test.q:680 src/t-test.q:703 src/t-test.q:828
+#: src/crosstabs.q:815 src/examine.q:858 src/frequencies.q:1453
+#: src/oneway.q:395 src/t-test.q:682 src/t-test.q:705 src/t-test.q:830
 #: src/t-test.q:1365
 msgid "N"
 msgstr ""
 
 #: src/t-test.q:1365
 msgid "N"
 msgstr ""
 
-#: src/crosstabs.q:785 src/examine.q:807 src/frequencies.q:1058
-#: src/frequencies.q:1059 src/frequencies.q:1060
+#: src/crosstabs.q:816 src/examine.q:860 src/frequencies.q:1085
+#: src/frequencies.q:1086 src/frequencies.q:1087
 msgid "Percent"
 msgstr ""
 
 msgid "Percent"
 msgstr ""
 
-#: src/crosstabs.q:1027
+#: src/crosstabs.q:1058
 msgid "count"
 msgstr ""
 
 msgid "count"
 msgstr ""
 
-#: src/crosstabs.q:1028
+#: src/crosstabs.q:1059
 msgid "row %"
 msgstr ""
 
 msgid "row %"
 msgstr ""
 
-#: src/crosstabs.q:1029
+#: src/crosstabs.q:1060
 msgid "column %"
 msgstr ""
 
 msgid "column %"
 msgstr ""
 
-#: src/crosstabs.q:1030
+#: src/crosstabs.q:1061
 msgid "total %"
 msgstr ""
 
 msgid "total %"
 msgstr ""
 
-#: src/crosstabs.q:1031
+#: src/crosstabs.q:1062
 msgid "expected"
 msgstr ""
 
 msgid "expected"
 msgstr ""
 
-#: src/crosstabs.q:1032
+#: src/crosstabs.q:1063
 msgid "residual"
 msgstr ""
 
 msgid "residual"
 msgstr ""
 
-#: src/crosstabs.q:1033
+#: src/crosstabs.q:1064
 msgid "std. resid."
 msgstr ""
 
 msgid "std. resid."
 msgstr ""
 
-#: src/crosstabs.q:1034
+#: src/crosstabs.q:1065
 msgid "adj. resid."
 msgstr ""
 
 msgid "adj. resid."
 msgstr ""
 
-#: src/crosstabs.q:1067 src/crosstabs.q:1094 src/crosstabs.q:1114
-#: src/crosstabs.q:1135 src/examine.q:448
+#: src/crosstabs.q:1098 src/crosstabs.q:1125 src/crosstabs.q:1145
+#: src/crosstabs.q:1166 src/examine.q:495
 msgid "Statistic"
 msgstr ""
 
 msgid "Statistic"
 msgstr ""
 
-#: src/crosstabs.q:1069 src/oneway.q:276 src/oneway.q:711 src/t-test.q:979
-#: src/t-test.q:1171 src/t-test.q:1263
+#: src/crosstabs.q:1100 src/oneway.q:278 src/oneway.q:707 src/t-test.q:980
+#: src/t-test.q:1172 src/t-test.q:1264
 msgid "df"
 msgstr ""
 
 msgid "df"
 msgstr ""
 
-#: src/crosstabs.q:1071
+#: src/crosstabs.q:1102
 msgid "Asymp. Sig. (2-sided)"
 msgstr ""
 
 msgid "Asymp. Sig. (2-sided)"
 msgstr ""
 
-#: src/crosstabs.q:1073
+#: src/crosstabs.q:1104
 msgid "Exact. Sig. (2-sided)"
 msgstr ""
 
 msgid "Exact. Sig. (2-sided)"
 msgstr ""
 
-#: src/crosstabs.q:1075
+#: src/crosstabs.q:1106
 msgid "Exact. Sig. (1-sided)"
 msgstr ""
 
 msgid "Exact. Sig. (1-sided)"
 msgstr ""
 
-#: src/crosstabs.q:1093 src/crosstabs.q:1134
+#: src/crosstabs.q:1124 src/crosstabs.q:1165
 msgid "Category"
 msgstr ""
 
 msgid "Category"
 msgstr ""
 
-#: src/crosstabs.q:1096 src/crosstabs.q:1138
+#: src/crosstabs.q:1127 src/crosstabs.q:1169
 msgid "Asymp. Std. Error"
 msgstr ""
 
 msgid "Asymp. Std. Error"
 msgstr ""
 
-#: src/crosstabs.q:1097 src/crosstabs.q:1139
+#: src/crosstabs.q:1128 src/crosstabs.q:1170
 msgid "Approx. T"
 msgstr ""
 
 msgid "Approx. T"
 msgstr ""
 
-#: src/crosstabs.q:1098 src/crosstabs.q:1140
+#: src/crosstabs.q:1129 src/crosstabs.q:1171
 msgid "Approx. Sig."
 msgstr ""
 
 msgid "Approx. Sig."
 msgstr ""
 
-#: src/crosstabs.q:1113
+#: src/crosstabs.q:1144
 #, c-format
 msgid " 95%% Confidence Interval"
 msgstr ""
 
 #, c-format
 msgid " 95%% Confidence Interval"
 msgstr ""
 
-#: src/crosstabs.q:1116 src/t-test.q:983 src/t-test.q:1168 src/t-test.q:1266
+#: src/crosstabs.q:1147 src/t-test.q:984 src/t-test.q:1169 src/t-test.q:1267
 msgid "Lower"
 msgstr ""
 
 msgid "Lower"
 msgstr ""
 
-#: src/crosstabs.q:1117 src/t-test.q:984 src/t-test.q:1169 src/t-test.q:1267
+#: src/crosstabs.q:1148 src/t-test.q:985 src/t-test.q:1170 src/t-test.q:1268
 msgid "Upper"
 msgstr ""
 
 msgid "Upper"
 msgstr ""
 
-#: src/crosstabs.q:1136
+#: src/crosstabs.q:1167
 msgid "Type"
 msgstr ""
 
 msgid "Type"
 msgstr ""
 
-#: src/crosstabs.q:1884
+#: src/crosstabs.q:1916
 msgid "Pearson Chi-Square"
 msgstr ""
 
 msgid "Pearson Chi-Square"
 msgstr ""
 
-#: src/crosstabs.q:1885
+#: src/crosstabs.q:1917
 msgid "Likelihood Ratio"
 msgstr ""
 
 msgid "Likelihood Ratio"
 msgstr ""
 
-#: src/crosstabs.q:1886
+#: src/crosstabs.q:1918
 msgid "Fisher's Exact Test"
 msgstr ""
 
 msgid "Fisher's Exact Test"
 msgstr ""
 
-#: src/crosstabs.q:1887
+#: src/crosstabs.q:1919
 msgid "Continuity Correction"
 msgstr ""
 
 msgid "Continuity Correction"
 msgstr ""
 
-#: src/crosstabs.q:1888
+#: src/crosstabs.q:1920
 msgid "Linear-by-Linear Association"
 msgstr ""
 
 msgid "Linear-by-Linear Association"
 msgstr ""
 
-#: src/crosstabs.q:1925 src/crosstabs.q:1995 src/crosstabs.q:2054
+#: src/crosstabs.q:1957 src/crosstabs.q:2027 src/crosstabs.q:2086
 msgid "N of Valid Cases"
 msgstr ""
 
 msgid "N of Valid Cases"
 msgstr ""
 
-#: src/crosstabs.q:1941 src/crosstabs.q:2070
+#: src/crosstabs.q:1973 src/crosstabs.q:2102
 msgid "Nominal by Nominal"
 msgstr ""
 
 msgid "Nominal by Nominal"
 msgstr ""
 
-#: src/crosstabs.q:1942 src/crosstabs.q:2071
+#: src/crosstabs.q:1974 src/crosstabs.q:2103
 msgid "Ordinal by Ordinal"
 msgstr ""
 
 msgid "Ordinal by Ordinal"
 msgstr ""
 
-#: src/crosstabs.q:1943
+#: src/crosstabs.q:1975
 msgid "Interval by Interval"
 msgstr ""
 
 msgid "Interval by Interval"
 msgstr ""
 
-#: src/crosstabs.q:1944
+#: src/crosstabs.q:1976
 msgid "Measure of Agreement"
 msgstr ""
 
 msgid "Measure of Agreement"
 msgstr ""
 
-#: src/crosstabs.q:1949
+#: src/crosstabs.q:1981
 msgid "Phi"
 msgstr ""
 
 msgid "Phi"
 msgstr ""
 
-#: src/crosstabs.q:1950
+#: src/crosstabs.q:1982
 msgid "Cramer's V"
 msgstr ""
 
 msgid "Cramer's V"
 msgstr ""
 
-#: src/crosstabs.q:1951
+#: src/crosstabs.q:1983
 msgid "Contingency Coefficient"
 msgstr ""
 
 msgid "Contingency Coefficient"
 msgstr ""
 
-#: src/crosstabs.q:1952
+#: src/crosstabs.q:1984
 msgid "Kendall's tau-b"
 msgstr ""
 
 msgid "Kendall's tau-b"
 msgstr ""
 
-#: src/crosstabs.q:1953
+#: src/crosstabs.q:1985
 msgid "Kendall's tau-c"
 msgstr ""
 
 msgid "Kendall's tau-c"
 msgstr ""
 
-#: src/crosstabs.q:1954
+#: src/crosstabs.q:1986
 msgid "Gamma"
 msgstr ""
 
 msgid "Gamma"
 msgstr ""
 
-#: src/crosstabs.q:1955
+#: src/crosstabs.q:1987
 msgid "Spearman Correlation"
 msgstr ""
 
 msgid "Spearman Correlation"
 msgstr ""
 
-#: src/crosstabs.q:1956
+#: src/crosstabs.q:1988
 msgid "Pearson's R"
 msgstr ""
 
 msgid "Pearson's R"
 msgstr ""
 
-#: src/crosstabs.q:1957
+#: src/crosstabs.q:1989
 msgid "Kappa"
 msgstr ""
 
 msgid "Kappa"
 msgstr ""
 
-#: src/crosstabs.q:2027
+#: src/crosstabs.q:2059
 #, c-format
 msgid "Odds Ratio for %s (%g / %g)"
 msgstr ""
 
 #, c-format
 msgid "Odds Ratio for %s (%g / %g)"
 msgstr ""
 
-#: src/crosstabs.q:2030
+#: src/crosstabs.q:2062
 #, c-format
 msgid "Odds Ratio for %s (%.*s / %.*s)"
 msgstr ""
 
 #, c-format
 msgid "Odds Ratio for %s (%.*s / %.*s)"
 msgstr ""
 
-#: src/crosstabs.q:2038
+#: src/crosstabs.q:2070
 #, c-format
 msgid "For cohort %s = %g"
 msgstr ""
 
 #, c-format
 msgid "For cohort %s = %g"
 msgstr ""
 
-#: src/crosstabs.q:2041
+#: src/crosstabs.q:2073
 #, c-format
 msgid "For cohort %s = %.*s"
 msgstr ""
 
 #, c-format
 msgid "For cohort %s = %.*s"
 msgstr ""
 
-#: src/crosstabs.q:2072
+#: src/crosstabs.q:2104
 msgid "Nominal by Interval"
 msgstr ""
 
 msgid "Nominal by Interval"
 msgstr ""
 
-#: src/crosstabs.q:2077
+#: src/crosstabs.q:2109
 msgid "Lambda"
 msgstr ""
 
 msgid "Lambda"
 msgstr ""
 
-#: src/crosstabs.q:2078
+#: src/crosstabs.q:2110
 msgid "Goodman and Kruskal tau"
 msgstr ""
 
 msgid "Goodman and Kruskal tau"
 msgstr ""
 
-#: src/crosstabs.q:2079
+#: src/crosstabs.q:2111
 msgid "Uncertainty Coefficient"
 msgstr ""
 
 msgid "Uncertainty Coefficient"
 msgstr ""
 
-#: src/crosstabs.q:2080
+#: src/crosstabs.q:2112
 msgid "Somers' d"
 msgstr ""
 
 msgid "Somers' d"
 msgstr ""
 
-#: src/crosstabs.q:2081
+#: src/crosstabs.q:2113
 msgid "Eta"
 msgstr ""
 
 msgid "Eta"
 msgstr ""
 
-#: src/crosstabs.q:2086
+#: src/crosstabs.q:2118
 msgid "Symmetric"
 msgstr ""
 
 msgid "Symmetric"
 msgstr ""
 
-#: src/crosstabs.q:2087 src/crosstabs.q:2088
+#: src/crosstabs.q:2119 src/crosstabs.q:2120
 #, c-format
 msgid "%s Dependent"
 msgstr ""
 
 #, c-format
 msgid "%s Dependent"
 msgstr ""
 
-#: src/examine.q:217 src/examine.q:229
+#: src/examine.q:264 src/examine.q:276
 #, c-format
 msgid "%s and %s are mutually exclusive"
 msgstr ""
 
 #, c-format
 msgid "%s and %s are mutually exclusive"
 msgstr ""
 
-#: src/examine.q:449 src/oneway.q:396 src/oneway.q:709
+#: src/examine.q:496 src/oneway.q:398 src/oneway.q:705
 msgid "Std. Error"
 msgstr ""
 
 msgid "Std. Error"
 msgstr ""
 
-#: src/examine.q:563 src/oneway.q:410
+#: src/examine.q:610 src/oneway.q:412
 msgid "Descriptives"
 msgstr ""
 
 msgid "Descriptives"
 msgstr ""
 
-#: src/examine.q:602 src/oneway.q:401
+#: src/examine.q:649 src/oneway.q:403
 #, c-format
 msgid "%g%% Confidence Interval for Mean"
 msgstr ""
 
 #, c-format
 msgid "%g%% Confidence Interval for Mean"
 msgstr ""
 
-#: src/examine.q:608 src/oneway.q:403
+#: src/examine.q:655 src/oneway.q:405
 msgid "Lower Bound"
 msgstr ""
 
 msgid "Lower Bound"
 msgstr ""
 
-#: src/examine.q:619 src/oneway.q:404
+#: src/examine.q:666 src/oneway.q:406
 msgid "Upper Bound"
 msgstr ""
 
 msgid "Upper Bound"
 msgstr ""
 
-#: src/examine.q:631
+#: src/examine.q:678
 msgid "5% Trimmed Mean"
 msgstr ""
 
 msgid "5% Trimmed Mean"
 msgstr ""
 
-#: src/examine.q:636 src/frequencies.q:102
+#: src/examine.q:689 src/frequencies.q:112
 msgid "Median"
 msgstr ""
 
 msgid "Median"
 msgstr ""
 
-#: src/examine.q:653 src/oneway.q:395 src/t-test.q:682 src/t-test.q:705
-#: src/t-test.q:829 src/t-test.q:1166
+#: src/examine.q:706 src/oneway.q:397 src/t-test.q:684 src/t-test.q:707
+#: src/t-test.q:831 src/t-test.q:1167
 msgid "Std. Deviation"
 msgstr ""
 
 msgid "Std. Deviation"
 msgstr ""
 
-#: src/examine.q:701
+#: src/examine.q:754
 msgid "Interquartile Range"
 msgstr ""
 
 msgid "Interquartile Range"
 msgstr ""
 
-#: src/examine.q:778
+#: src/examine.q:831
 msgid "Case Processing Summary"
 msgstr ""
 
 msgid "Case Processing Summary"
 msgstr ""
 
-#: src/examine.q:1103
+#: src/examine.q:1157
 msgid "Extreme Values"
 msgstr ""
 
 msgid "Extreme Values"
 msgstr ""
 
-#: src/examine.q:1126
+#: src/examine.q:1180
 msgid "Case Number"
 msgstr ""
 
 msgid "Case Number"
 msgstr ""
 
-#: src/examine.q:1252
+#: src/examine.q:1306
 msgid "Highest"
 msgstr ""
 
 msgid "Highest"
 msgstr ""
 
-#: src/examine.q:1257
+#: src/examine.q:1311
 msgid "Lowest"
 msgstr ""
 
 msgid "Lowest"
 msgstr ""
 
-#: src/file-handle.q:125
+#: src/examine.q:1350
+#, c-format
+msgid "Normal Q-Q Plot of %s"
+msgstr ""
+
+#: src/examine.q:1351 src/examine.q:1357
+msgid "Observed Value"
+msgstr ""
+
+#: src/examine.q:1352
+msgid "Expected Normal"
+msgstr ""
+
+#: src/examine.q:1355
+#, c-format
+msgid "Detrended Normal Q-Q Plot of %s"
+msgstr ""
+
+#: src/examine.q:1358
+msgid "Dev from Normal"
+msgstr ""
+
+#: src/file-handle.q:122
 #, c-format
 msgid ""
 "File handle %s already refers to file %s.  File handle cannot be redefined "
 "within a session."
 msgstr ""
 
 #, c-format
 msgid ""
 "File handle %s already refers to file %s.  File handle cannot be redefined "
 "within a session."
 msgstr ""
 
-#: src/file-handle.q:147
+#: src/file-handle.q:144
 msgid "The FILE HANDLE required subcommand NAME is not present."
 msgstr ""
 
 msgid "The FILE HANDLE required subcommand NAME is not present."
 msgstr ""
 
-#: src/file-handle.q:166
+#: src/file-handle.q:163
 msgid ""
 "Fixed-length records were specified on /RECFORM, but record length was not "
 "specified on /LRECL.  Assuming 1024-character records."
 msgstr ""
 
 msgid ""
 "Fixed-length records were specified on /RECFORM, but record length was not "
 "specified on /LRECL.  Assuming 1024-character records."
 msgstr ""
 
-#: src/file-handle.q:173
+#: src/file-handle.q:170
 #, c-format
 msgid ""
 "Record length (%ld) must be at least one byte.  1-character records will be "
 "assumed."
 msgstr ""
 
 #, c-format
 msgid ""
 "Record length (%ld) must be at least one byte.  1-character records will be "
 "assumed."
 msgstr ""
 
-#: src/file-handle.q:247
-msgid "<Inline File>"
+#: src/file-handle.q:260
+#, c-format
+msgid "Can't open %s as a %s because it is already open as a %s"
+msgstr ""
+
+#: src/file-handle.q:264
+#, c-format
+msgid "Can't open %s as a %s for %s because it is already open for %s"
+msgstr ""
+
+#: src/file-handle.q:269
+#, c-format
+msgid "Can't re-open %s as a %s for %s"
 msgstr ""
 
 msgstr ""
 
-#: src/file-handle.q:262
+#: src/file-handle.q:317
 msgid "expecting a file name or handle name"
 msgstr ""
 
 msgid "expecting a file name or handle name"
 msgstr ""
 
-#: src/frequencies.q:101
+#: src/frequencies.q:111
 msgid "S.E. Mean"
 msgstr ""
 
 msgid "S.E. Mean"
 msgstr ""
 
-#: src/frequencies.q:103
+#: src/frequencies.q:113
 msgid "Mode"
 msgstr ""
 
 msgid "Mode"
 msgstr ""
 
-#: src/frequencies.q:107
+#: src/frequencies.q:117
 msgid "S.E. Kurt"
 msgstr ""
 
 msgid "S.E. Kurt"
 msgstr ""
 
-#: src/frequencies.q:109
+#: src/frequencies.q:119
 msgid "S.E. Skew"
 msgstr ""
 
 msgid "S.E. Skew"
 msgstr ""
 
-#: src/frequencies.q:315
+#: src/frequencies.q:340
 msgid ""
 "At most one of BARCHART, HISTOGRAM, or HBAR should be given.  HBAR will be "
 "assumed.  Argument values will be given precedence increasing along the "
 "order given."
 msgstr ""
 
 msgid ""
 "At most one of BARCHART, HISTOGRAM, or HBAR should be given.  HBAR will be "
 "assumed.  Argument values will be given precedence increasing along the "
 "order given."
 msgstr ""
 
-#: src/frequencies.q:398
+#: src/frequencies.q:423
 #, c-format
 msgid ""
 "MAX must be greater than or equal to MIN, if both are specified.  However, "
 "MIN was specified as %g and MAX as %g.  MIN and MAX will be ignored."
 msgstr ""
 
 #, c-format
 msgid ""
 "MAX must be greater than or equal to MIN, if both are specified.  However, "
 "MIN was specified as %g and MAX as %g.  MIN and MAX will be ignored."
 msgstr ""
 
-#: src/frequencies.q:722
+#: src/frequencies.q:747
 msgid ""
 "Upper limit of integer mode value range must be greater than lower limit."
 msgstr ""
 
 msgid ""
 "Upper limit of integer mode value range must be greater than lower limit."
 msgstr ""
 
-#: src/frequencies.q:734
+#: src/frequencies.q:760
 #, c-format
 msgid "Variable %s specified multiple times on VARIABLES subcommand."
 msgstr ""
 
 #, c-format
 msgid "Variable %s specified multiple times on VARIABLES subcommand."
 msgstr ""
 
-#: src/frequencies.q:747
+#: src/frequencies.q:766
 #, c-format
 msgid "Integer mode specified, but %s is not a numeric variable."
 msgstr ""
 
 #, c-format
 msgid "Integer mode specified, but %s is not a numeric variable."
 msgstr ""
 
-#: src/frequencies.q:809
+#: src/frequencies.q:832
 msgid "`)' expected after GROUPED interval list."
 msgstr ""
 
 msgid "`)' expected after GROUPED interval list."
 msgstr ""
 
-#: src/frequencies.q:822
+#: src/frequencies.q:844
 #, c-format
 msgid "Variables %s specified on GROUPED but not on VARIABLES."
 msgstr ""
 
 #, c-format
 msgid "Variables %s specified on GROUPED but not on VARIABLES."
 msgstr ""
 
-#: src/frequencies.q:825
+#: src/frequencies.q:851
 #, c-format
 msgid "Variables %s specified multiple times on GROUPED subcommand."
 msgstr ""
 
 #, c-format
 msgid "Variables %s specified multiple times on GROUPED subcommand."
 msgstr ""
 
-#: src/frequencies.q:1055 src/frequencies.q:1146 src/frequencies.q:1147
-#: src/frequencies.q:1177
+#: src/frequencies.q:1082 src/frequencies.q:1174 src/frequencies.q:1175
+#: src/frequencies.q:1207
 msgid "Cum"
 msgstr ""
 
 msgid "Cum"
 msgstr ""
 
-#: src/frequencies.q:1057
+#: src/frequencies.q:1084
 msgid "Frequency"
 msgstr ""
 
 msgid "Frequency"
 msgstr ""
 
-#: src/frequencies.q:1076
+#: src/frequencies.q:1104
 msgid "Value Label"
 msgstr ""
 
 msgid "Value Label"
 msgstr ""
 
-#: src/frequencies.q:1175
+#: src/frequencies.q:1205
 msgid "Freq"
 msgstr ""
 
 msgid "Freq"
 msgstr ""
 
-#: src/frequencies.q:1176 src/frequencies.q:1178
+#: src/frequencies.q:1206 src/frequencies.q:1208
 msgid "Pct"
 msgstr ""
 
 msgid "Pct"
 msgstr ""
 
-#: src/frequencies.q:1394
+#: src/frequencies.q:1427
 #, c-format
 msgid "No valid data for variable %s; statistics not displayed."
 msgstr ""
 
 #, c-format
 msgid "No valid data for variable %s; statistics not displayed."
 msgstr ""
 
-#: src/frequencies.q:1433
+#: src/frequencies.q:1465
 msgid "Percentiles"
 msgstr ""
 
 msgid "Percentiles"
 msgstr ""
 
-#: src/list.q:148
+#: src/list.q:150
 #, c-format
 msgid ""
 "The first case (%ld) specified precedes the last case (%ld) specified.  The "
 "values will be swapped."
 msgstr ""
 
 #, c-format
 msgid ""
 "The first case (%ld) specified precedes the last case (%ld) specified.  The "
 "values will be swapped."
 msgstr ""
 
-#: src/list.q:156
+#: src/list.q:158
 #, c-format
 msgid ""
 "The first case (%ld) to list is less than 1.  The value is being reset to 1."
 msgstr ""
 
 #, c-format
 msgid ""
 "The first case (%ld) to list is less than 1.  The value is being reset to 1."
 msgstr ""
 
-#: src/list.q:162
+#: src/list.q:164
 #, c-format
 msgid ""
 "The last case (%ld) to list is less than 1.  The value is being reset to 1."
 msgstr ""
 
 #, c-format
 msgid ""
 "The last case (%ld) to list is less than 1.  The value is being reset to 1."
 msgstr ""
 
-#: src/list.q:168
+#: src/list.q:170
 #, c-format
 msgid "The step value %ld is less than 1.  The value is being reset to 1."
 msgstr ""
 
 #, c-format
 msgid "The step value %ld is less than 1.  The value is being reset to 1."
 msgstr ""
 
-#: src/list.q:195
+#: src/list.q:197
 msgid "`/FORMAT WEIGHT' specified, but weighting is not on."
 msgstr ""
 
 msgid "`/FORMAT WEIGHT' specified, but weighting is not on."
 msgstr ""
 
-#: src/list.q:436
+#: src/list.q:438
 msgid "Line"
 msgstr ""
 
 msgid "Line"
 msgstr ""
 
-#: src/means.q:101
+#: src/means.q:100
 msgid "Missing required subcommand TABLES."
 msgstr ""
 
 msgid "Missing required subcommand TABLES."
 msgstr ""
 
-#: src/means.q:135
-msgid "TABLES or CROSSBREAK subcommand may not appear more than once."
-msgstr ""
-
-#: src/means.q:172
-#, c-format
-msgid ""
-"Variable %s specified on TABLES or CROSSBREAK, but not specified on "
-"VARIABLES."
-msgstr ""
-
-#: src/means.q:186
-#, c-format
-msgid "LOWEST and HIGHEST may not be used for independent variables (%s)."
-msgstr ""
-
-#: src/means.q:194
-#, c-format
-msgid ""
-"Independent variables (%s) may not have noninteger endpoints in their ranges."
-msgstr ""
-
-#: src/means.q:227
-msgid "VARIABLES must precede TABLES."
-msgstr ""
-
-#: src/means.q:284
-#, c-format
-msgid "Upper value (%g) is less than lower value (%g) on VARIABLES subcommand."
+#: src/means.q:134
+msgid "TABLES subcommand may not appear more than once."
 msgstr ""
 
 msgstr ""
 
-#: src/oneway.q:166
+#: src/oneway.q:168
 msgid "Number of contrast coefficients must equal the number of groups"
 msgstr ""
 
 msgid "Number of contrast coefficients must equal the number of groups"
 msgstr ""
 
-#: src/oneway.q:175
+#: src/oneway.q:177
 #, c-format
 msgid "Coefficients for contrast %d do not total zero"
 msgstr ""
 
 #, c-format
 msgid "Coefficients for contrast %d do not total zero"
 msgstr ""
 
-#: src/oneway.q:240 src/t-test.q:364 src/t-test.q:449
+#: src/oneway.q:242 src/t-test.q:366 src/t-test.q:451
 #, c-format
 msgid "`%s' is not a variable name"
 msgstr ""
 
 #, c-format
 msgid "`%s' is not a variable name"
 msgstr ""
 
-#: src/oneway.q:275
+#: src/oneway.q:277
 msgid "Sum of Squares"
 msgstr ""
 
 msgid "Sum of Squares"
 msgstr ""
 
-#: src/oneway.q:277
+#: src/oneway.q:279
 msgid "Mean Square"
 msgstr ""
 
 msgid "Mean Square"
 msgstr ""
 
-#: src/oneway.q:278 src/t-test.q:976
+#: src/oneway.q:280 src/t-test.q:977
 msgid "F"
 msgstr ""
 
 msgid "F"
 msgstr ""
 
-#: src/oneway.q:279 src/oneway.q:549
+#: src/oneway.q:281 src/oneway.q:552
 msgid "Significance"
 msgstr ""
 
 msgid "Significance"
 msgstr ""
 
-#: src/oneway.q:304
+#: src/oneway.q:305
 msgid "Between Groups"
 msgstr ""
 
 msgid "Between Groups"
 msgstr ""
 
-#: src/oneway.q:305
+#: src/oneway.q:306
 msgid "Within Groups"
 msgstr ""
 
 msgid "Within Groups"
 msgstr ""
 
-#: src/oneway.q:351
+#: src/oneway.q:353
 msgid "ANOVA"
 msgstr ""
 
 msgid "ANOVA"
 msgstr ""
 
-#: src/oneway.q:546
+#: src/oneway.q:549
 msgid "Levene Statistic"
 msgstr ""
 
 msgid "Levene Statistic"
 msgstr ""
 
-#: src/oneway.q:547
+#: src/oneway.q:550
 msgid "df1"
 msgstr ""
 
 msgid "df1"
 msgstr ""
 
-#: src/oneway.q:548
+#: src/oneway.q:551
 msgid "df2"
 msgstr ""
 
 msgid "df2"
 msgstr ""
 
-#: src/oneway.q:552
+#: src/oneway.q:555
 msgid "Test of Homogeneity of Variances"
 msgstr ""
 
 msgid "Test of Homogeneity of Variances"
 msgstr ""
 
-#: src/oneway.q:628
+#: src/oneway.q:631
 msgid "Contrast Coefficients"
 msgstr ""
 
 msgid "Contrast Coefficients"
 msgstr ""
 
-#: src/oneway.q:630 src/oneway.q:707
+#: src/oneway.q:633 src/oneway.q:703
 msgid "Contrast"
 msgstr ""
 
 msgid "Contrast"
 msgstr ""
 
-#: src/oneway.q:705
+#: src/oneway.q:701
 msgid "Contrast Tests"
 msgstr ""
 
 msgid "Contrast Tests"
 msgstr ""
 
-#: src/oneway.q:708
+#: src/oneway.q:704
 msgid "Value of Contrast"
 msgstr ""
 
 msgid "Value of Contrast"
 msgstr ""
 
-#: src/oneway.q:710 src/t-test.q:978 src/t-test.q:1170 src/t-test.q:1262
+#: src/oneway.q:706 src/t-test.q:979 src/t-test.q:1171 src/t-test.q:1263
 msgid "t"
 msgstr ""
 
 msgid "t"
 msgstr ""
 
-#: src/oneway.q:712 src/t-test.q:980 src/t-test.q:1172 src/t-test.q:1264
+#: src/oneway.q:708 src/t-test.q:981 src/t-test.q:1173 src/t-test.q:1265
 msgid "Sig. (2-tailed)"
 msgstr ""
 
 msgid "Sig. (2-tailed)"
 msgstr ""
 
-#: src/oneway.q:760
+#: src/oneway.q:754
 msgid "Assume equal variances"
 msgstr ""
 
 msgid "Assume equal variances"
 msgstr ""
 
-#: src/oneway.q:764
+#: src/oneway.q:758
 msgid "Does not assume equal"
 msgstr ""
 
 msgid "Does not assume equal"
 msgstr ""
 
@@ -4748,115 +4624,115 @@ msgstr ""
 msgid "data> "
 msgstr ""
 
 msgid "data> "
 msgstr ""
 
-#: src/t-test.q:266
+#: src/t-test.q:268
 msgid "TESTVAL, GROUPS and PAIRS subcommands are mutually exclusive."
 msgstr ""
 
 msgid "TESTVAL, GROUPS and PAIRS subcommands are mutually exclusive."
 msgstr ""
 
-#: src/t-test.q:283
+#: src/t-test.q:285
 msgid "VARIABLES subcommand is not appropriate with PAIRS"
 msgstr ""
 
 msgid "VARIABLES subcommand is not appropriate with PAIRS"
 msgstr ""
 
-#: src/t-test.q:320
+#: src/t-test.q:322
 msgid "One or more VARIABLES must be specified."
 msgstr ""
 
 msgid "One or more VARIABLES must be specified."
 msgstr ""
 
-#: src/t-test.q:377
+#: src/t-test.q:379
 #, c-format
 msgid "Long string variable %s is not valid here."
 msgstr ""
 
 #, c-format
 msgid "Long string variable %s is not valid here."
 msgstr ""
 
-#: src/t-test.q:397
+#: src/t-test.q:399
 msgid ""
 "When applying GROUPS to a string variable, at least one value must be "
 "specified."
 msgstr ""
 
 msgid ""
 "When applying GROUPS to a string variable, at least one value must be "
 "specified."
 msgstr ""
 
-#: src/t-test.q:484
+#: src/t-test.q:486
 #, c-format
 msgid ""
 "PAIRED was specified but the number of variables preceding WITH (%d) did not "
 "match the number following (%d)."
 msgstr ""
 
 #, c-format
 msgid ""
 "PAIRED was specified but the number of variables preceding WITH (%d) did not "
 "match the number following (%d)."
 msgstr ""
 
-#: src/t-test.q:501
+#: src/t-test.q:503
 msgid "At least two variables must be specified on PAIRS."
 msgstr ""
 
 msgid "At least two variables must be specified on PAIRS."
 msgstr ""
 
-#: src/t-test.q:678
+#: src/t-test.q:680
 msgid "One-Sample Statistics"
 msgstr ""
 
 msgid "One-Sample Statistics"
 msgstr ""
 
-#: src/t-test.q:683 src/t-test.q:706 src/t-test.q:830
+#: src/t-test.q:685 src/t-test.q:708 src/t-test.q:832
 msgid "SE. Mean"
 msgstr ""
 
 msgid "SE. Mean"
 msgstr ""
 
-#: src/t-test.q:700
+#: src/t-test.q:702
 msgid "Group Statistics"
 msgstr ""
 
 msgid "Group Statistics"
 msgstr ""
 
-#: src/t-test.q:824
+#: src/t-test.q:826
 msgid "Paired Sample Statistics"
 msgstr ""
 
 msgid "Paired Sample Statistics"
 msgstr ""
 
-#: src/t-test.q:846 src/t-test.q:1191 src/t-test.q:1382
+#: src/t-test.q:848 src/t-test.q:1192 src/t-test.q:1382
 #, c-format
 msgid "Pair %d"
 msgstr ""
 
 #, c-format
 msgid "Pair %d"
 msgstr ""
 
-#: src/t-test.q:964
+#: src/t-test.q:965
 msgid "Independent Samples Test"
 msgstr ""
 
 msgid "Independent Samples Test"
 msgstr ""
 
-#: src/t-test.q:972
+#: src/t-test.q:973
 msgid "Levene's Test for Equality of Variances"
 msgstr ""
 
 msgid "Levene's Test for Equality of Variances"
 msgstr ""
 
-#: src/t-test.q:974
+#: src/t-test.q:975
 msgid "t-test for Equality of Means"
 msgstr ""
 
 msgid "t-test for Equality of Means"
 msgstr ""
 
-#: src/t-test.q:977 src/t-test.q:1367
+#: src/t-test.q:978 src/t-test.q:1367
 msgid "Sig."
 msgstr ""
 
 msgid "Sig."
 msgstr ""
 
-#: src/t-test.q:981 src/t-test.q:1265
+#: src/t-test.q:982 src/t-test.q:1266
 msgid "Mean Difference"
 msgstr ""
 
 msgid "Mean Difference"
 msgstr ""
 
-#: src/t-test.q:982
+#: src/t-test.q:983
 msgid "Std. Error Difference"
 msgstr ""
 
 msgid "Std. Error Difference"
 msgstr ""
 
-#: src/t-test.q:987 src/t-test.q:1162 src/t-test.q:1257
+#: src/t-test.q:988 src/t-test.q:1163 src/t-test.q:1258
 #, c-format
 msgid "%g%% Confidence Interval of the Difference"
 msgstr ""
 
 #, c-format
 msgid "%g%% Confidence Interval of the Difference"
 msgstr ""
 
-#: src/t-test.q:1041
+#: src/t-test.q:1043
 msgid "Equal variances assumed"
 msgstr ""
 
 msgid "Equal variances assumed"
 msgstr ""
 
-#: src/t-test.q:1094
+#: src/t-test.q:1095
 msgid "Equal variances not assumed"
 msgstr ""
 
 msgid "Equal variances not assumed"
 msgstr ""
 
-#: src/t-test.q:1152
+#: src/t-test.q:1153
 msgid "Paired Samples Test"
 msgstr ""
 
 msgid "Paired Samples Test"
 msgstr ""
 
-#: src/t-test.q:1155
+#: src/t-test.q:1156
 msgid "Paired Differences"
 msgstr ""
 
 msgid "Paired Differences"
 msgstr ""
 
-#: src/t-test.q:1167
+#: src/t-test.q:1168
 msgid "Std. Error Mean"
 msgstr ""
 
 msgid "Std. Error Mean"
 msgstr ""
 
-#: src/t-test.q:1246
+#: src/t-test.q:1247
 msgid "One-Sample Test"
 msgstr ""
 
 msgid "One-Sample Test"
 msgstr ""
 
-#: src/t-test.q:1251
+#: src/t-test.q:1252
 #, c-format
 msgid "Test Value = %f"
 msgstr ""
 #, c-format
 msgid "Test Value = %f"
 msgstr ""
index 87780b84698cbc12af4284e97d647f267dacadff..3f30425b495f324205b7618cde743aeb9e2c3425 100644 (file)
@@ -2,10 +2,12 @@ Makefile
 Makefile.in
 correlations.c
 crosstabs.c
 Makefile.in
 correlations.c
 crosstabs.c
+examine.c
 file-handle.c
 frequencies.c
 list.c
 means.c
 file-handle.c
 frequencies.c
 list.c
 means.c
+oneway.c
 pspp
 q2c
 set.c
 pspp
 q2c
 set.c
index 6adfe8d6fab81c761b67d4752c471c89444b80e6..1ba99e1a0714a9148fa295c532a614f0137a93ab 100644 (file)
@@ -1,3 +1,229 @@
+Mon Nov 15 01:33:32 2004  Ben Pfaff  <blp@gnu.org>
+
+       * q2c.c: (dump_header) Don't try to emit #includes at very top of
+       output file because that will precede #include <config.h>, which
+       is bad.
+       (main) Add needed headers to /* (header) */ code.
+
+Mon Nov 15 01:21:36 2004  Ben Pfaff  <blp@gnu.org>
+
+       Instead of making system or portable file readers responsible for
+       dropping and reordering variables, make them read full cases and
+       let the caller take care of any changes.
+
+       * get.c: New "case map" structure to handle this.  Use for GET,
+       IMPORT, MATCH FILES.  Essentially rewrite the whole file.
+
+       * pfm-read.c: (pfm_read_case) Read into provided case.  Signature
+       changed appropriately.
+
+       * sfm-read.c: (sfm_read_case) Ditto.
+
+Mon Nov 15 00:47:45 2004  Ben Pfaff  <blp@gnu.org>
+
+       Decided that case_serialize() and case_unserialize() were too
+       abstract.  Also we need a couple more functions to avoid excessive
+       copying for data in/out fast paths.
+
+       * case.c: (case_serial_size) Removed.
+       (case_serialize) Rename case_to_values() and make its argument
+       explicitly an array of union values.
+       (case_unserialize) Rename case_from_values() and make its argument
+       explicitly an array of union values.
+       (case_data_all) New function.
+       (case_data_all_rw) New function.
+
+       * casefile.c: (struct casefile) Change buffer from array of
+       unsigned char to array of union value for better accuracy.
+       Redefine buffer_used and buffer_size in terms of values, not
+       bytes.  Remove case_size because it is now redundant with
+       value_cnt.  Fix up all references to these members.
+
+Mon Nov 15 00:45:46 2004  Ben Pfaff  <blp@gnu.org>
+
+       * barchart.c: (struct subcat) Make `label' member const to silence
+       GCC warning with -Wwrite-strings.
+
+       * cartesian.c: (struct dataset) Ditto.
+
+       * case.c: Don't re-define NDEBUG if already defined.
+       Add lots of comments.
+
+       * str.c: Fix includes.
+
+       * crosstabs.q: Fix includes.
+
+       * examine.q: Fix includes.  Fix GCC warning about unused
+       variables.
+       
+       * frequencies.q: (stat macro) Removed and replaced where used by
+       its expansion.
+
+       * list.q: Fix includes.
+
+       * oneway.q: Fix includes.
+
+       * piechart.c: Fix includes.  Only define M_PI if not already
+       defined.
+
+       * sfm-read.c: (bswap) New function.
+       (bswap_int32) Write in terms of bswap.
+       (bswap_flt64) Ditto.
+
+       * str.c: (ds_data) Add external definition here, needed because
+       str.h has only an `extern inline' version.
+
+       * value-labels.c: Fix includes.
+
+Mon Nov 15 00:40:55 2004  Ben Pfaff  <blp@gnu.org>
+
+       Instead of providing a system or portable file writer with a raw
+       case in the format needed for output, provide it with a regular
+       case.  The writer takes care of any needed translation.
+
+       * aggregate.c: Adopt new scheme for AGGREGATE.
+       (struct agr_proc) sfm_agr_case member removed.
+       (write_case_to_sfm) Removed because the new interface is easier to
+       use.
+
+       * get.c: Adopt new scheme for SAVE, XSAVE, EXPORT.
+
+       * pfm-write.c: Implement new scheme.
+
+       * sfm-write.c: Ditto.
+
+Mon Nov 15 00:32:24 2004  Ben Pfaff  <blp@gnu.org>
+
+       Instead of treating `struct file_handle' as a class to subclass
+       into data files, system files, and portable files, instead use it
+       as a helper that coordinates access.  Now it is opaque, too.
+
+       This means that most references to a struct file_handle are now
+       changed into references to one of struct dfm_reader, struct
+       dfm_writer, struct sfm_reader, struct sfm_writer, struct
+       pfm_reader, or struct pfm_writer, according to what's being read
+       or written.
+
+       Most related changes are only worth summarizing briefly.
+
+       * dictionary.c: (dict_clear) Destroy aux data in deleted
+       variables.
+       (dict_clear_aux) New function.
+       (dict_create_var) Initialize aux, aux_dtor.
+       (dict_delete_var) Destroy aux data in deleted variable.
+
+       * file-handle.h: (struct fh_ext_class) Removed.
+       (struct file_handle) Removed.
+       (fh_init_files) Removed.
+
+       * file-handle.q: Changed references to a handle's `private' member
+       to direct references.
+       (struct private_file_handle) Renamed file_handle.
+       Add next, open_cnt, type, open_mode, aux members.
+       (struct file_handle_list) Removed.
+       (extern var inline_file) Removed.
+       (static var file_handles) Changed from file_handle_list * to
+       file_handle *.
+       (create_file_handle) Initialize new members.
+       (fh_close_handle) Removed.
+       (mode_name) New function.
+       (fh_open) New function.
+       (fh_close) New function.
+       (fh_parse_file_handle) Renamed fh_parse().
+
+       * glob.c: (init_glob) Remove fh_init_files() call.
+       
+       * aggregate.c: use sfm_writer.
+       (create_sysfile) Removed because the new interface is simpler.
+       
+       * apply-dict.c: Use sfm_reader.
+
+       * data-list.c: Use dfm_reader.
+
+       * file-type.c: Use dfm_reader.
+
+       * get.c: Use sfm_reader, sfm_writer, pfm_reader, pfm_writer.
+
+       * inpt-pgm.c: Use dfm_reader.
+
+       * print.c: Use dfm_writer.
+
+       * sysfile-info: Use sfm_reader.
+
+       * dfm-read.c: Adopt new file handle infrastructure.
+
+       * dfm-write.c: Ditto.
+
+       * pfm-read.c: Ditto.
+       
+       * pfm-write.c: Ditto.
+
+       * sfm-read.c: Ditto.
+
+       * sfm-write.c: Ditto.
+       
+Mon Nov 15 00:31:44 2004  Ben Pfaff  <blp@gnu.org>
+
+       Break dictionary functions into separate header file.
+
+       * dictionary.h: New file.
+
+       * var.h: Moved dict_*() functions to dictionary.h.
+
+Mon Nov 15 00:30:33 2004  Ben Pfaff  <blp@gnu.org>
+
+       Get rid of procedure-specific union in struct variable, using
+       instead a void * pointer and a destructor function.
+
+       Most related changes are only worth brief summaries.
+
+       * crosstabs.q: Fix includes.  Use new struct var_range in lieu of
+       old p.crs member in struct variable.
+       
+       * frequencies.q: Fix includes.  Use new struct var_freqs in lieu
+       of old p.frq member in struct variable.
+
+       * histogram.c: (draw_histogram) Takes new freq_tab arg because
+       it's no longer possible to grab this from var->p.frq.
+
+       * piechart.c: (draw_piechart) Ditto.
+
+       * group.c: (group_proc_get) New function.
+
+       * levene.c: Use group_proc_get() in lieu of old p.grp_data member
+       in struct variable.
+
+       * oneway.q: Ditto.
+
+       * t-test.q: Ditto.
+
+       * main.c: (execute_command) Clear aux data in default_dict after
+       each command.  (It's debatable whether this should be done.)
+
+       * matrix-data.c: Use new struct mxd_var in lieu of old p.mxd
+       member in struct variable.
+
+       * means.q: Get rid of integer mode, which is not included in
+       recent SPSS and was the only code that wanted per-variable private
+       data.
+
+       * var.h: (struct crosstab_proc) Removed.
+       (struct frequencies_proc) Removed.
+       (struct list_proc) Removed.
+       (struct get_proc) Removed.
+       (struct means_proc) Removed.
+       (struct matrix_data_proc) Removed.
+       (struct match_files_proc) Removed.
+       (lots of enums) Removed.
+       (struct variable) Removed members `p', `get'.  Add member
+       `aux_dtor'.
+
+       * vars-atr.c: (var_attach_aux) New function.
+       (var_detach_aux) New function.
+       (var_clear_aux) New function.
+       (var_dtor_free) New function.
+       (discard_variables) Use NULL instead of inline_file.
+
 Fri Nov 12 10:07:11 WST 2004 John Darrington <john@darrington.wattle.id.au>
 
        * value-labs.c  Fixed the implmentation of value_to_string, so 
 Fri Nov 12 10:07:11 WST 2004 John Darrington <john@darrington.wattle.id.au>
 
        * value-labs.c  Fixed the implmentation of value_to_string, so 
index 0320792f9f841954ca3c41e58a359a745a251623..ec5ce0aeb155197614b6bdd7557eb66e48ce6a9b 100644 (file)
@@ -54,7 +54,8 @@ case.c case.h \
 casefile.c casefile.h cmdline.c cmdline.h command.c command.def                \
 command.h compute.c copyleft.c copyleft.h count.c data-in.c data-in.h  \
 data-list.c data-list.h data-out.c date.c debug-print.h descript.c     \
 casefile.c casefile.h cmdline.c cmdline.h command.c command.def                \
 command.h compute.c copyleft.c copyleft.h count.c data-in.c data-in.h  \
 data-list.c data-list.h data-out.c date.c debug-print.h descript.c     \
-devind.c devind.h dfm.c dfm.h dictionary.c do-if.c do-ifP.h error.c    \
+devind.c devind.h dfm-read.c dfm-read.h dfm-write.c dfm-write.h        \
+dictionary.c dictionary.h do-if.c do-ifP.h error.c \
 error.h expr-evl.c expr-opt.c expr-prs.c expr.h exprP.h expr.def        \
 factor_stats.c factor_stats.h file-handle.h    \
 file-type.c filename.c filename.h flip.c font.h format.c format.def    \
 error.h expr-evl.c expr-opt.c expr-prs.c expr.h exprP.h expr.def        \
 factor_stats.c factor_stats.h file-handle.h    \
 file-type.c filename.c filename.h flip.c font.h format.c format.def    \
@@ -63,10 +64,12 @@ groff-font.c group.c group.h group_proc.h \
 hash.c hash.h html.c htmlP.h include.c inpt-pgm.c lexer.c      \
 lexer.h levene.c levene.h log.h loop.c magic.c magic.h main.c main.h   \
 matrix-data.c mis-val.c misc.c misc.h modify-vars.c                    \
 hash.c hash.h html.c htmlP.h include.c inpt-pgm.c lexer.c      \
 lexer.h levene.c levene.h log.h loop.c magic.c magic.h main.c main.h   \
 matrix-data.c mis-val.c misc.c misc.h modify-vars.c                    \
-moments.c moments.h numeric.c output.c output.h pfm-read.c pfm-write.c \
-pfm.h pool.c pool.h postscript.c print.c random.c random.h recode.c    \
+moments.c moments.h numeric.c output.c output.h pfm-read.c pfm-read.h  \
+pfm-write.c pfm-write.h \
+pool.c pool.h postscript.c print.c random.c random.h recode.c  \
 rename-vars.c repeat.c repeat.h sample.c sel-if.c settings.h           \
 rename-vars.c repeat.c repeat.h sample.c sel-if.c settings.h           \
-sfm-read.c sfm-write.c sfm.h sfmP.h som.c som.h sort.c sort.h          \
+sfm-read.c sfm-read.h sfm-write.c sfm-write.h sfmP.h som.c som.h       \
+sort.c sort.h \
 split-file.c str.c str.h subclist.c subclist.h \
 sysfile-info.c tab.c tab.h temporary.c stat.h \
 title.c  val.h val-labs.c value-labels.c value-labels.h                \
 split-file.c str.c str.h subclist.c subclist.h \
 sysfile-info.c tab.c tab.h temporary.c stat.h \
 title.c  val.h val-labs.c value-labels.c value-labels.h                \
index ec601dbffdba49806bb2d2c4c62482a1ae084d41..ffd29166142cb2bd44512ebb80cb17d2b935f7f6 100644 (file)
@@ -24,6 +24,7 @@
 #include "case.h"
 #include "casefile.h"
 #include "command.h"
 #include "case.h"
 #include "casefile.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "file-handle.h"
 #include "lexer.h"
 #include "error.h"
 #include "file-handle.h"
 #include "lexer.h"
@@ -31,7 +32,7 @@
 #include "moments.h"
 #include "pool.h"
 #include "settings.h"
 #include "moments.h"
 #include "pool.h"
 #include "settings.h"
-#include "sfm.h"
+#include "sfm-write.h"
 #include "sort.h"
 #include "str.h"
 #include "var.h"
 #include "sort.h"
 #include "str.h"
 #include "var.h"
@@ -116,7 +117,7 @@ enum missing_treatment
 struct agr_proc 
   {
     /* We have either an output file or a sink. */
 struct agr_proc 
   {
     /* We have either an output file or a sink. */
-    struct file_handle *out_file;       /* Output file, or null if none. */
+    struct sfm_writer *writer;          /* Output file, or null if none. */
     struct case_sink *sink;             /* Sink, or null if none. */
 
     /* Break variables. */
     struct case_sink *sink;             /* Sink, or null if none. */
 
     /* Break variables. */
@@ -130,7 +131,6 @@ struct agr_proc
     struct dictionary *dict;            /* Aggregate dictionary. */
     int case_cnt;                       /* Counts aggregated cases. */
     struct ccase agr_case;              /* Aggregate case for output. */
     struct dictionary *dict;            /* Aggregate dictionary. */
     int case_cnt;                       /* Counts aggregated cases. */
     struct ccase agr_case;              /* Aggregate case for output. */
-    flt64 *sfm_agr_case;                /* Aggregate case in SFM format. */
   };
 
 static void initialize_aggregate_info (struct agr_proc *);
   };
 
 static void initialize_aggregate_info (struct agr_proc *);
@@ -142,13 +142,11 @@ static int aggregate_single_case (struct agr_proc *agr,
                                   const struct ccase *input,
                                   struct ccase *output);
 static void dump_aggregate_info (struct agr_proc *agr, struct ccase *output);
                                   const struct ccase *input,
                                   struct ccase *output);
 static void dump_aggregate_info (struct agr_proc *agr, struct ccase *output);
-static int create_sysfile (struct agr_proc *);
 
 /* Aggregating to the active file. */
 static int agr_to_active_file (struct ccase *, void *aux);
 
 /* Aggregating to a system file. */
 
 /* Aggregating to the active file. */
 static int agr_to_active_file (struct ccase *, void *aux);
 
 /* Aggregating to a system file. */
-static void write_case_to_sfm (struct agr_proc *agr);
 static int presorted_agr_to_sysfile (struct ccase *, void *aux);
 \f
 /* Parsing. */
 static int presorted_agr_to_sysfile (struct ccase *, void *aux);
 \f
 /* Parsing. */
@@ -158,11 +156,12 @@ int
 cmd_aggregate (void)
 {
   struct agr_proc agr;
 cmd_aggregate (void)
 {
   struct agr_proc agr;
+  struct file_handle *out_file = NULL;
 
   /* Have we seen these subcommands? */
   unsigned seen = 0;
 
 
   /* Have we seen these subcommands? */
   unsigned seen = 0;
 
-  agr.out_file = NULL;
+  agr.writer = NULL;
   agr.sink = NULL;
   agr.missing = ITEMWISE;
   agr.sort = NULL;
   agr.sink = NULL;
   agr.missing = ITEMWISE;
   agr.sort = NULL;
@@ -186,18 +185,16 @@ cmd_aggregate (void)
          if (seen & 1)
            {
              msg (SE, _("%s subcommand given multiple times."),"OUTFILE");
          if (seen & 1)
            {
              msg (SE, _("%s subcommand given multiple times."),"OUTFILE");
-              goto lossage;
+              goto error;
            }
          seen |= 1;
              
          lex_match ('=');
            }
          seen |= 1;
              
          lex_match ('=');
-         if (lex_match ('*'))
-           agr.out_file = NULL;
-         else 
+         if (!lex_match ('*'))
            {
            {
-             agr.out_file = fh_parse_file_handle ();
-             if (agr.out_file == NULL)
-                goto lossage;
+             out_file = fh_parse ();
+             if (out_file == NULL)
+                goto error;
            }
        }
       else if (lex_match_id ("MISSING"))
            }
        }
       else if (lex_match_id ("MISSING"))
@@ -206,7 +203,7 @@ cmd_aggregate (void)
          if (!lex_match_id ("COLUMNWISE"))
            {
              lex_error (_("while expecting COLUMNWISE"));
          if (!lex_match_id ("COLUMNWISE"))
            {
              lex_error (_("while expecting COLUMNWISE"));
-              goto lossage;
+              goto error;
            }
          agr.missing = COLUMNWISE;
        }
            }
          agr.missing = COLUMNWISE;
        }
@@ -221,7 +218,7 @@ cmd_aggregate (void)
          if (seen & 8)
            {
              msg (SE, _("%s subcommand given multiple times."),"BREAK");
          if (seen & 8)
            {
              msg (SE, _("%s subcommand given multiple times."),"BREAK");
-              goto lossage;
+              goto error;
            }
          seen |= 8;
 
            }
          seen |= 8;
 
@@ -229,7 +226,7 @@ cmd_aggregate (void)
           agr.sort = sort_parse_criteria (default_dict,
                                           &agr.break_vars, &agr.break_var_cnt);
           if (agr.sort == NULL)
           agr.sort = sort_parse_criteria (default_dict,
                                           &agr.break_vars, &agr.break_var_cnt);
           if (agr.sort == NULL)
-            goto lossage;
+            goto error;
          
           for (i = 0; i < agr.break_var_cnt; i++)
             {
          
           for (i = 0; i < agr.break_var_cnt; i++)
             {
@@ -247,7 +244,7 @@ cmd_aggregate (void)
       
   /* Read in the aggregate functions. */
   if (!parse_aggregate_functions (&agr))
       
   /* Read in the aggregate functions. */
   if (!parse_aggregate_functions (&agr))
-    goto lossage;
+    goto error;
 
   /* Delete documents. */
   if (!(seen & 2))
 
   /* Delete documents. */
   if (!(seen & 2))
@@ -262,7 +259,7 @@ cmd_aggregate (void)
   initialize_aggregate_info (&agr);
 
   /* Output to active file or external file? */
   initialize_aggregate_info (&agr);
 
   /* Output to active file or external file? */
-  if (agr.out_file == NULL) 
+  if (out_file == NULL) 
     {
       /* The active file will be replaced by the aggregated data,
          so TEMPORARY is moot. */
     {
       /* The active file will be replaced by the aggregated data,
          so TEMPORARY is moot. */
@@ -289,9 +286,10 @@ cmd_aggregate (void)
     }
   else
     {
     }
   else
     {
-      if (!create_sysfile (&agr))
-        goto lossage;
-
+      agr.writer = sfm_open_writer (out_file, agr.dict, get_scompression ());
+      if (agr.writer == NULL)
+        goto error;
+      
       if (agr.sort != NULL && (seen & 4) == 0) 
         {
           /* Sorting is needed. */
       if (agr.sort != NULL && (seen & 4) == 0) 
         {
           /* Sorting is needed. */
@@ -301,12 +299,12 @@ cmd_aggregate (void)
           
           dst = sort_active_file_to_casefile (agr.sort);
           if (dst == NULL)
           
           dst = sort_active_file_to_casefile (agr.sort);
           if (dst == NULL)
-            goto lossage;
+            goto error;
           reader = casefile_get_destructive_reader (dst);
           while (casereader_read_xfer (reader, &c)) 
             {
               if (aggregate_single_case (&agr, &c, &agr.agr_case)) 
           reader = casefile_get_destructive_reader (dst);
           while (casereader_read_xfer (reader, &c)) 
             {
               if (aggregate_single_case (&agr, &c, &agr.agr_case)) 
-                write_case_to_sfm (&agr);
+                sfm_write_case (agr.writer, &agr.agr_case);
               case_destroy (&c);
             }
           casereader_destroy (reader);
               case_destroy (&c);
             }
           casereader_destroy (reader);
@@ -321,36 +319,18 @@ cmd_aggregate (void)
       if (agr.case_cnt > 0) 
         {
           dump_aggregate_info (&agr, &agr.agr_case);
       if (agr.case_cnt > 0) 
         {
           dump_aggregate_info (&agr, &agr.agr_case);
-          write_case_to_sfm (&agr);
+          sfm_write_case (agr.writer, &agr.agr_case);
         }
         }
-      fh_close_handle (agr.out_file);
     }
   
   agr_destroy (&agr);
   return CMD_SUCCESS;
 
     }
   
   agr_destroy (&agr);
   return CMD_SUCCESS;
 
-lossage:
+error:
   agr_destroy (&agr);
   return CMD_FAILURE;
 }
 
   agr_destroy (&agr);
   return CMD_FAILURE;
 }
 
-/* Create a system file for use in aggregation to an external
-   file. */
-static int
-create_sysfile (struct agr_proc *agr)
-{
-  struct sfm_write_info w;
-  w.h = agr->out_file;
-  w.dict = agr->dict;
-  w.compress = get_scompression();
-  if (!sfm_write_dictionary (&w))
-    return 0;
-
-  agr->sfm_agr_case = xmalloc (sizeof *agr->sfm_agr_case * w.case_size);
-    
-  return 1;
-}
-
 /* Parse all the aggregate functions. */
 static int
 parse_aggregate_functions (struct agr_proc *agr)
 /* Parse all the aggregate functions. */
 static int
 parse_aggregate_functions (struct agr_proc *agr)
@@ -390,8 +370,9 @@ parse_aggregate_functions (struct agr_proc *agr)
        {
          int n_dest_prev = n_dest;
          
        {
          int n_dest_prev = n_dest;
          
-         if (!parse_DATA_LIST_vars (&dest, &n_dest, PV_APPEND | PV_SINGLE | PV_NO_SCRATCH))
-           goto lossage;
+         if (!parse_DATA_LIST_vars (&dest, &n_dest,
+                                     PV_APPEND | PV_SINGLE | PV_NO_SCRATCH))
+           goto error;
 
          /* Assign empty labels. */
          {
 
          /* Assign empty labels. */
          {
@@ -414,7 +395,7 @@ parse_aggregate_functions (struct agr_proc *agr)
       if (token != T_ID)
        {
          lex_error (_("expecting aggregation function"));
       if (token != T_ID)
        {
          lex_error (_("expecting aggregation function"));
-         goto lossage;
+         goto error;
        }
 
       include_missing = 0;
        }
 
       include_missing = 0;
@@ -430,7 +411,7 @@ parse_aggregate_functions (struct agr_proc *agr)
       if (NULL == function->name)
        {
          msg (SE, _("Unknown aggregation function %s."), tokid);
       if (NULL == function->name)
        {
          msg (SE, _("Unknown aggregation function %s."), tokid);
-         goto lossage;
+         goto error;
        }
       func_index = function - agr_func_tab;
       lex_get ();
        }
       func_index = function - agr_func_tab;
       lex_get ();
@@ -445,7 +426,7 @@ parse_aggregate_functions (struct agr_proc *agr)
          else
            {
              lex_error (_("expecting `('"));
          else
            {
              lex_error (_("expecting `('"));
-             goto lossage;
+             goto error;
            }
        } else {
          /* Parse list of source variables. */
            }
        } else {
          /* Parse list of source variables. */
@@ -458,7 +439,7 @@ parse_aggregate_functions (struct agr_proc *agr)
              pv_opts |= PV_SAME_TYPE;
 
            if (!parse_variables (default_dict, &src, &n_src, pv_opts))
              pv_opts |= PV_SAME_TYPE;
 
            if (!parse_variables (default_dict, &src, &n_src, pv_opts))
-             goto lossage;
+             goto error;
          }
 
          /* Parse function arguments, for those functions that
          }
 
          /* Parse function arguments, for those functions that
@@ -480,7 +461,7 @@ parse_aggregate_functions (struct agr_proc *agr)
                    type = NUMERIC;
                  } else {
                    msg (SE, _("Missing argument %d to %s."), i + 1, function->name);
                    type = NUMERIC;
                  } else {
                    msg (SE, _("Missing argument %d to %s."), i + 1, function->name);
-                   goto lossage;
+                   goto error;
                  }
            
                lex_get ();
                  }
            
                lex_get ();
@@ -490,7 +471,7 @@ parse_aggregate_functions (struct agr_proc *agr)
                    msg (SE, _("Arguments to %s must be of same type as "
                               "source variables."),
                         function->name);
                    msg (SE, _("Arguments to %s must be of same type as "
                               "source variables."),
                         function->name);
-                   goto lossage;
+                   goto error;
                  }
              }
 
                  }
              }
 
@@ -498,7 +479,7 @@ parse_aggregate_functions (struct agr_proc *agr)
          if (!lex_match(')'))
            {
              lex_error (_("expecting `)'"));
          if (!lex_match(')'))
            {
              lex_error (_("expecting `)'"));
-             goto lossage;
+             goto error;
            }
          
          /* Now check that the number of source variables match the
            }
          
          /* Now check that the number of source variables match the
@@ -512,7 +493,7 @@ parse_aggregate_functions (struct agr_proc *agr)
              msg (SE, _("Number of source variables (%d) does not match "
                         "number of target variables (%d)."),
                   n_src, n_dest);
              msg (SE, _("Number of source variables (%d) does not match "
                         "number of target variables (%d)."),
                   n_src, n_dest);
-             goto lossage;
+             goto error;
            }
        }
        
            }
        }
        
@@ -584,7 +565,7 @@ parse_aggregate_functions (struct agr_proc *agr)
                           "variables."),
                     dest[i]);
                free (dest[i]);
                           "variables."),
                     dest[i]);
                free (dest[i]);
-               goto lossage;
+               goto error;
              }
 
            free (dest[i]);
              }
 
            free (dest[i]);
@@ -636,7 +617,7 @@ parse_aggregate_functions (struct agr_proc *agr)
        }
       continue;
       
        }
       continue;
       
-    lossage:
+    error:
       for (i = 0; i < n_dest; i++)
        {
          free (dest[i]);
       for (i = 0; i < n_dest; i++)
        {
          free (dest[i]);
@@ -664,11 +645,11 @@ agr_destroy (struct agr_proc *agr)
 {
   struct agr_var *iter, *next;
 
 {
   struct agr_var *iter, *next;
 
-  if (agr->dict != NULL)
-    dict_destroy (agr->dict);
+  sfm_close_writer (agr->writer);
   if (agr->sort != NULL)
     sort_destroy_criteria (agr->sort);
   free (agr->break_vars);
   if (agr->sort != NULL)
     sort_destroy_criteria (agr->sort);
   free (agr->break_vars);
+  free (agr->prev_break);
   for (iter = agr->agr_vars; iter; iter = next)
     {
       next = iter->next;
   for (iter = agr->agr_vars; iter; iter = next)
     {
       next = iter->next;
@@ -687,7 +668,8 @@ agr_destroy (struct agr_proc *agr)
         moments1_destroy (iter->moments);
       free (iter);
     }
         moments1_destroy (iter->moments);
       free (iter);
     }
-  free (agr->prev_break);
+  if (agr->dict != NULL)
+    dict_destroy (agr->dict);
   case_destroy (&agr->agr_case);
 }
 \f
   case_destroy (&agr->agr_case);
 }
 \f
@@ -1143,38 +1125,6 @@ agr_to_active_file (struct ccase *c, void *agr_)
   return 1;
 }
 
   return 1;
 }
 
-/* Writes AGR->agr_case to AGR->out_file. */
-static void
-write_case_to_sfm (struct agr_proc *agr)
-{
-  flt64 *p;
-  int i;
-
-  p = agr->sfm_agr_case;
-  for (i = 0; i < dict_get_var_cnt (agr->dict); i++)
-    {
-      struct variable *v = dict_get_var (agr->dict, i);
-      
-      if (v->type == NUMERIC)
-       {
-         double src = case_num (&agr->agr_case, v->fv);
-         if (src == SYSMIS)
-           *p++ = -FLT64_MAX;
-         else
-           *p++ = src;
-       }
-      else
-       {
-         memcpy (p, case_str (&agr->agr_case, v->fv), v->width);
-         memset (&((char *) p)[v->width], ' ',
-                 REM_RND_UP (v->width, sizeof (flt64)));
-         p += DIV_RND_UP (v->width, sizeof (flt64));
-       }
-    }
-
-  sfm_write_case (agr->out_file, agr->sfm_agr_case, p - agr->sfm_agr_case);
-}
-
 /* Aggregate the current case and output it if we passed a
    breakpoint. */
 static int
 /* Aggregate the current case and output it if we passed a
    breakpoint. */
 static int
@@ -1183,7 +1133,7 @@ presorted_agr_to_sysfile (struct ccase *c, void *agr_)
   struct agr_proc *agr = agr_;
 
   if (aggregate_single_case (agr, c, &agr->agr_case)) 
   struct agr_proc *agr = agr_;
 
   if (aggregate_single_case (agr, c, &agr->agr_case)) 
-    write_case_to_sfm (agr);
+    sfm_write_case (agr->writer, &agr->agr_case);
 
   return 1;
 }
 
   return 1;
 }
index 1aaba30579d4bae63477671a2943c9ed923a9366..a0c2fbac5bbf4a79648cb2663f1bcddc99855228 100644 (file)
@@ -19,9 +19,9 @@
 
 #include <config.h>
 #include "alloc.h"
 
 #include <config.h>
 #include "alloc.h"
-#include "error.h"
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include "error.h"
 #include "str.h"
 \f
 /* Public functions. */
 #include "str.h"
 \f
 /* Public functions. */
index 9f6867cc4ea3da247a04a677dbd9b8733126edab..4cc8c10379644585db7b33f358854e9d6be59c05 100644 (file)
 #include <config.h>
 #include <stdlib.h>
 #include "command.h"
 #include <config.h>
 #include <stdlib.h>
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "file-handle.h"
 #include "hash.h"
 #include "lexer.h"
 #include "error.h"
 #include "file-handle.h"
 #include "hash.h"
 #include "lexer.h"
-#include "sfm.h"
+#include "sfm-read.h"
 #include "str.h"
 #include "value-labels.h"
 #include "var.h"
 #include "str.h"
 #include "value-labels.h"
 #include "var.h"
@@ -36,6 +37,7 @@ int
 cmd_apply_dictionary (void)
 {
   struct file_handle *handle;
 cmd_apply_dictionary (void)
 {
   struct file_handle *handle;
+  struct sfm_reader *reader;
   struct dictionary *dict;
 
   int n_matched = 0;
   struct dictionary *dict;
 
   int n_matched = 0;
@@ -44,13 +46,14 @@ cmd_apply_dictionary (void)
   
   lex_match_id ("FROM");
   lex_match ('=');
   
   lex_match_id ("FROM");
   lex_match ('=');
-  handle = fh_parse_file_handle ();
+  handle = fh_parse ();
   if (!handle)
     return CMD_FAILURE;
 
   if (!handle)
     return CMD_FAILURE;
 
-  dict = sfm_read_dictionary (handle, NULL);
+  reader = sfm_open_reader (handle, &dict, NULL);
   if (dict == NULL)
     return CMD_FAILURE;
   if (dict == NULL)
     return CMD_FAILURE;
+  sfm_close_reader (reader);
 
   for (i = 0; i < dict_get_var_cnt (dict); i++)
     {
 
   for (i = 0; i < dict_get_var_cnt (dict); i++)
     {
@@ -170,7 +173,7 @@ cmd_apply_dictionary (void)
         dict_set_weight (default_dict, new_weight);
     }
   
         dict_set_weight (default_dict, new_weight);
     }
   
-  sfm_maybe_close (handle);
+  sfm_close_reader (reader);
 
   return lex_end_of_command ();
 }
 
   return lex_end_of_command ();
 }
index 5bf12bc0a58f70248541f66d026bc33c2a709da1..a28b13bdcac0e8c3cd2a6257f4019de448324a6f 100644 (file)
@@ -23,6 +23,7 @@
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "hash.h"
 #include "lexer.h"
 #include "error.h"
 #include "hash.h"
 #include "lexer.h"
index e47a541ae0257dd087fd7f160a36d35f8b078cec..26fdfb5e5ac32ef7de647e2dc44d855a6ab9d9b5 100644 (file)
@@ -89,7 +89,7 @@ static const char subcat_name[]="Gender";
 
 struct subcat {
   const double *data;
 
 struct subcat {
   const double *data;
-  char   *label;
+  const char *label;
 };
 
 static const struct subcat sub_catagory[SUB_CATAGORIES] = 
 };
 
 static const struct subcat sub_catagory[SUB_CATAGORIES] = 
index a2396abcf765da48037458e03e14c3f5ed2c7f70..34e1a3dd0d50c03ada8c6352772c25bcd75b364b 100644 (file)
@@ -27,7 +27,7 @@
 struct dataset
 {
   int n_data;
 struct dataset
 {
   int n_data;
-  char *label;
+  const char *label;
 };
 
 
 };
 
 
index 6c427df5bc4dedcbdca0413cb62357b2300fe257..21fb0bdbfdb725a58cd73f5640e5ed8c5f9ebee7 100644 (file)
 #ifdef GLOBAL_DEBUGGING
 #undef NDEBUG
 #else
 #ifdef GLOBAL_DEBUGGING
 #undef NDEBUG
 #else
+#ifndef NDEBUG
 #define NDEBUG
 #endif
 #define NDEBUG
 #endif
+#endif
 #include <assert.h>
 
 #include <assert.h>
 
+/* Changes C not to share data with any other case.
+   C must be a case with a reference count greater than 1.
+   There should be no reason for external code to call this
+   function explicitly.  It will be called automatically when
+   needed. */
 void
 case_unshare (struct ccase *c) 
 {
 void
 case_unshare (struct ccase *c) 
 {
@@ -49,6 +56,8 @@ case_unshare (struct ccase *c)
           sizeof *cd->values * cd->value_cnt); 
 }
 
           sizeof *cd->values * cd->value_cnt); 
 }
 
+/* Returns the number of bytes needed by a case with VALUE_CNT
+   values. */
 static inline size_t
 case_size (size_t value_cnt) 
 {
 static inline size_t
 case_size (size_t value_cnt) 
 {
@@ -57,6 +66,7 @@ case_size (size_t value_cnt)
 }
 
 #ifdef GLOBAL_DEBUGGING
 }
 
 #ifdef GLOBAL_DEBUGGING
+/* Initializes C as a null case. */
 void
 case_nullify (struct ccase *c) 
 {
 void
 case_nullify (struct ccase *c) 
 {
@@ -66,6 +76,7 @@ case_nullify (struct ccase *c)
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
+/* Returns true iff C is a null case. */
 int
 case_is_null (const struct ccase *c) 
 {
 int
 case_is_null (const struct ccase *c) 
 {
@@ -73,6 +84,9 @@ case_is_null (const struct ccase *c)
 }
 #endif /* GLOBAL_DEBUGGING */
 
 }
 #endif /* GLOBAL_DEBUGGING */
 
+/* Initializes C as a new case that can store VALUE_CNT values.
+   The values have indeterminate contents until explicitly
+   written. */
 void
 case_create (struct ccase *c, size_t value_cnt) 
 {
 void
 case_create (struct ccase *c, size_t value_cnt) 
 {
@@ -81,6 +95,7 @@ case_create (struct ccase *c, size_t value_cnt)
 }
 
 #ifdef GLOBAL_DEBUGGING
 }
 
 #ifdef GLOBAL_DEBUGGING
+/* Initializes CLONE as a copy of ORIG. */
 void
 case_clone (struct ccase *clone, const struct ccase *orig)
 {
 void
 case_clone (struct ccase *clone, const struct ccase *orig)
 {
@@ -100,6 +115,8 @@ case_clone (struct ccase *clone, const struct ccase *orig)
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
+/* Replaces DST by SRC and nullifies SRC.
+   DST and SRC must be initialized cases at entry. */
 void
 case_move (struct ccase *dst, struct ccase *src) 
 {
 void
 case_move (struct ccase *dst, struct ccase *src) 
 {
@@ -116,6 +133,7 @@ case_move (struct ccase *dst, struct ccase *src)
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
+/* Destroys case C. */
 void
 case_destroy (struct ccase *c) 
 {
 void
 case_destroy (struct ccase *c) 
 {
@@ -134,6 +152,9 @@ case_destroy (struct ccase *c)
 }
 #endif /* GLOBAL_DEBUGGING */
 
 }
 #endif /* GLOBAL_DEBUGGING */
 
+/* Attempts to create C as a new case that holds VALUE_CNT
+   values.  Returns nonzero if successful, zero if memory
+   allocation failed. */
 int
 case_try_create (struct ccase *c, size_t value_cnt) 
 {
 int
 case_try_create (struct ccase *c, size_t value_cnt) 
 {
@@ -156,6 +177,9 @@ case_try_create (struct ccase *c, size_t value_cnt)
     }
 }
 
     }
 }
 
+/* Tries to initialize CLONE as a copy of ORIG.
+   Returns nonzero if successful, zero if memory allocation
+   failed. */
 int
 case_try_clone (struct ccase *clone, const struct ccase *orig) 
 {
 int
 case_try_clone (struct ccase *clone, const struct ccase *orig) 
 {
@@ -164,6 +188,8 @@ case_try_clone (struct ccase *clone, const struct ccase *orig)
 }
 
 #ifdef GLOBAL_DEBUGGING
 }
 
 #ifdef GLOBAL_DEBUGGING
+/* Copies VALUE_CNT values from SRC (starting at SRC_IDX) to DST
+   (starting at DST_IDX). */
 void
 case_copy (struct ccase *dst, size_t dst_idx,
            const struct ccase *src, size_t src_idx,
 void
 case_copy (struct ccase *dst, size_t dst_idx,
            const struct ccase *src, size_t src_idx,
@@ -191,50 +217,51 @@ case_copy (struct ccase *dst, size_t dst_idx,
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
-size_t
-case_serial_size (size_t value_cnt) 
-{
-  return value_cnt * sizeof (union value);
-}
-#endif /* GLOBAL_DEBUGGING */
-
-#ifdef GLOBAL_DEBUGGING
+/* Copies case C to OUTPUT.
+   OUTPUT_SIZE is the number of `union values' in OUTPUT,
+   which must match the number of `union values' in C. */
 void
 void
-case_serialize (const struct ccase *c, void *output,
+case_to_values (const struct ccase *c, union value *output,
                 size_t output_size UNUSED) 
 {
   assert (c != NULL);
   assert (c->this == c);
   assert (c->case_data != NULL);
   assert (c->case_data->ref_cnt > 0);
                 size_t output_size UNUSED) 
 {
   assert (c != NULL);
   assert (c->this == c);
   assert (c->case_data != NULL);
   assert (c->case_data->ref_cnt > 0);
-  assert (output_size == case_serial_size (c->case_data->value_cnt));
+  assert (output_size == c->case_data->value_cnt);
   assert (output != NULL || output_size == 0);
 
   memcpy (output, c->case_data->values,
   assert (output != NULL || output_size == 0);
 
   memcpy (output, c->case_data->values,
-          case_serial_size (c->case_data->value_cnt));
+          c->case_data->value_cnt * sizeof *output);
 }
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
 }
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
+/* Copies INPUT into case C.
+   INPUT_SIZE is the number of `union values' in INPUT,
+   which must match the number of `union values' in C. */
 void
 void
-case_unserialize (struct ccase *c, const void *input,
+case_from_values (struct ccase *c, const union value *input,
                   size_t input_size UNUSED) 
 {
   assert (c != NULL);
   assert (c->this == c);
   assert (c->case_data != NULL);
   assert (c->case_data->ref_cnt > 0);
                   size_t input_size UNUSED) 
 {
   assert (c != NULL);
   assert (c->this == c);
   assert (c->case_data != NULL);
   assert (c->case_data->ref_cnt > 0);
-  assert (input_size == case_serial_size (c->case_data->value_cnt));
+  assert (input_size == c->case_data->value_cnt);
   assert (input != NULL || input_size == 0);
 
   if (c->case_data->ref_cnt > 1)
     case_unshare (c);
   memcpy (c->case_data->values, input,
   assert (input != NULL || input_size == 0);
 
   if (c->case_data->ref_cnt > 1)
     case_unshare (c);
   memcpy (c->case_data->values, input,
-          case_serial_size (c->case_data->value_cnt));
+          c->case_data->value_cnt * sizeof *input);
 }
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
 }
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
+/* Returns a pointer to the `union value' used for the
+   element of C numbered IDX.
+   The caller must not modify the returned data. */
 const union value *
 case_data (const struct ccase *c, size_t idx) 
 {
 const union value *
 case_data (const struct ccase *c, size_t idx) 
 {
@@ -249,6 +276,8 @@ case_data (const struct ccase *c, size_t idx)
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
+/* Returns the numeric value of the `union value' in C numbered
+   IDX. */
 double
 case_num (const struct ccase *c, size_t idx) 
 {
 double
 case_num (const struct ccase *c, size_t idx) 
 {
@@ -263,6 +292,10 @@ case_num (const struct ccase *c, size_t idx)
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
+/* Returns the string value of the `union value' in C numbered
+   IDX.
+   (Note that the value is not null-terminated.)
+   The caller must not modify the return value. */
 const char *
 case_str (const struct ccase *c, size_t idx) 
 {
 const char *
 case_str (const struct ccase *c, size_t idx) 
 {
@@ -277,6 +310,9 @@ case_str (const struct ccase *c, size_t idx)
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
+/* Returns a pointer to the `union value' used for the
+   element of C numbered IDX.
+   The caller is allowed to modify the returned data. */
 union value *
 case_data_rw (struct ccase *c, size_t idx) 
 {
 union value *
 case_data_rw (struct ccase *c, size_t idx) 
 {
@@ -291,3 +327,37 @@ case_data_rw (struct ccase *c, size_t idx)
   return &c->case_data->values[idx];
 }
 #endif /* GLOBAL_DEBUGGING */
   return &c->case_data->values[idx];
 }
 #endif /* GLOBAL_DEBUGGING */
+
+/* Returns a pointer to the array of `union value's used for C.
+   The caller must *not* modify the returned data.
+
+   NOTE: This function breaks the case abstraction.  It should
+   *not* be used often.  Prefer the other case functions. */
+const union value *
+case_data_all (const struct ccase *c) 
+{
+  assert (c != NULL);
+  assert (c->this == c);
+  assert (c->case_data != NULL);
+  assert (c->case_data->ref_cnt > 0);
+
+  return c->case_data->values;
+}
+
+/* Returns a pointer to the array of `union value's used for C.
+   The caller is allowed to modify the returned data.
+
+   NOTE: This function breaks the case abstraction.  It should
+   *not* be used often.  Prefer the other case functions. */
+union value *
+case_data_all_rw (struct ccase *c) 
+{
+  assert (c != NULL);
+  assert (c->this == c);
+  assert (c->case_data != NULL);
+  assert (c->case_data->ref_cnt > 0);
+
+  if (c->case_data->ref_cnt > 1)
+    case_unshare (c);
+  return c->case_data->values;
+}
index 9e8b6e4fadcdfd3437087529886f4c4b781dbcc2..7312aa9458942e9b6780e47b78476c56887ad46b 100644 (file)
    case_move() or case_clone() instead of copying.  */
 struct ccase 
   {
    case_move() or case_clone() instead of copying.  */
 struct ccase 
   {
-    struct case_data *case_data;
+    struct case_data *case_data;        /* Actual data. */
 #if GLOBAL_DEBUGGING
 #if GLOBAL_DEBUGGING
-    struct ccase *this;
+    struct ccase *this;                 /* Detects unauthorized move/copy. */
 #endif
   };
 
 /* Invisible to user code. */
 struct case_data
   {
 #endif
   };
 
 /* Invisible to user code. */
 struct case_data
   {
-    size_t value_cnt;
-    unsigned ref_cnt;
-    union value values[1];
+    size_t value_cnt;                   /* Number of values. */
+    unsigned ref_cnt;                   /* Reference count. */
+    union value values[1];              /* Values. */
   };
 
 #ifdef GLOBAL_DEBUGGING
   };
 
 #ifdef GLOBAL_DEBUGGING
@@ -63,9 +63,9 @@ CASE_INLINE void case_copy (struct ccase *dst, size_t dst_idx,
                             const struct ccase *src, size_t src_idx,
                             size_t cnt);
 
                             const struct ccase *src, size_t src_idx,
                             size_t cnt);
 
-CASE_INLINE size_t case_serial_size (size_t value_cnt);
-CASE_INLINE void case_serialize (const struct ccase *, void *, size_t);
-CASE_INLINE void case_unserialize (struct ccase *, const void *, size_t);
+CASE_INLINE void case_to_values (const struct ccase *, union value *, size_t);
+CASE_INLINE void case_from_values (struct ccase *,
+                                   const union value *, size_t);
 
 CASE_INLINE const union value *case_data (const struct ccase *, size_t idx);
 CASE_INLINE double case_num (const struct ccase *, size_t idx);
 
 CASE_INLINE const union value *case_data (const struct ccase *, size_t idx);
 CASE_INLINE double case_num (const struct ccase *, size_t idx);
@@ -73,6 +73,9 @@ CASE_INLINE const char *case_str (const struct ccase *, size_t idx);
 
 CASE_INLINE union value *case_data_rw (struct ccase *, size_t idx);
 
 
 CASE_INLINE union value *case_data_rw (struct ccase *, size_t idx);
 
+const union value *case_data_all (const struct ccase *);
+union value *case_data_all_rw (struct ccase *);
+
 void case_unshare (struct ccase *);
 
 #ifndef GLOBAL_DEBUGGING
 void case_unshare (struct ccase *);
 
 #ifndef GLOBAL_DEBUGGING
@@ -126,28 +129,22 @@ case_copy (struct ccase *dst, size_t dst_idx,
              sizeof *dst->case_data->values * value_cnt); 
 }
 
              sizeof *dst->case_data->values * value_cnt); 
 }
 
-static inline size_t
-case_serial_size (size_t value_cnt) 
-{
-  return value_cnt * sizeof (union value);
-}
-
 static inline void
 static inline void
-case_serialize (const struct ccase *c, void *output,
+case_to_values (const struct ccase *c, union value *output,
                 size_t output_size UNUSED) 
 {
   memcpy (output, c->case_data->values,
                 size_t output_size UNUSED) 
 {
   memcpy (output, c->case_data->values,
-          case_serial_size (c->case_data->value_cnt));
+          c->case_data->value_cnt * sizeof *output);
 }
 
 static inline void
 }
 
 static inline void
-case_unserialize (struct ccase *c, const void *input,
+case_from_values (struct ccase *c, const union value *input,
                   size_t input_size UNUSED) 
 {
   if (c->case_data->ref_cnt > 1)
     case_unshare (c);
   memcpy (c->case_data->values, input,
                   size_t input_size UNUSED) 
 {
   if (c->case_data->ref_cnt > 1)
     case_unshare (c);
   memcpy (c->case_data->values, input,
-          case_serial_size (c->case_data->value_cnt));
+          c->case_data->value_cnt * sizeof *input);
 }
 
 static inline const union value *
 }
 
 static inline const union value *
index 2ff3a5742f3492ff65c719c38ebb6ebe03c96219..03828b7194b228e85f2c109b352ae6b23dc57126 100644 (file)
@@ -37,7 +37,7 @@
 #include <valgrind/valgrind.h>
 #endif
 
 #include <valgrind/valgrind.h>
 #endif
 
-#define IO_BUF_SIZE 8192
+#define IO_BUF_SIZE (8192 / sizeof (union value))
 
 /* A casefile is a sequentially accessible array of immutable
    cases.  It may be stored in memory or on disk as workspace
 
 /* A casefile is a sequentially accessible array of immutable
    cases.  It may be stored in memory or on disk as workspace
@@ -57,7 +57,6 @@ struct casefile
     /* Basic data. */
     struct casefile *next, *prev;       /* Next, prev in global list. */
     size_t value_cnt;                   /* Case size in `union value's. */
     /* Basic data. */
     struct casefile *next, *prev;       /* Next, prev in global list. */
     size_t value_cnt;                   /* Case size in `union value's. */
-    size_t case_size;                   /* Case size in bytes. */
     size_t case_acct_size;              /* Case size for accounting. */
     unsigned long case_cnt;             /* Number of cases stored. */
     enum { MEMORY, DISK } storage;      /* Where cases are stored. */
     size_t case_acct_size;              /* Case size for accounting. */
     unsigned long case_cnt;             /* Number of cases stored. */
     enum { MEMORY, DISK } storage;      /* Where cases are stored. */
@@ -71,9 +70,9 @@ struct casefile
     /* Disk storage. */
     int fd;                             /* File descriptor, -1 if none. */
     char *filename;                     /* Filename. */
     /* Disk storage. */
     int fd;                             /* File descriptor, -1 if none. */
     char *filename;                     /* Filename. */
-    unsigned char *buffer;              /* I/O buffer, NULL if none. */
-    size_t buffer_used;                 /* Number of bytes used in buffer. */
-    size_t buffer_size;                 /* Buffer size in bytes. */
+    union value *buffer;                /* I/O buffer, NULL if none. */
+    size_t buffer_used;                 /* Number of values used in buffer. */
+    size_t buffer_size;                 /* Buffer size in values. */
   };
 
 /* For reading out the cases in a casefile. */
   };
 
 /* For reading out the cases in a casefile. */
@@ -86,8 +85,8 @@ struct casereader
 
     /* Disk storage. */
     int fd;                             /* File descriptor. */
 
     /* Disk storage. */
     int fd;                             /* File descriptor. */
-    unsigned char *buffer;              /* I/O buffer. */
-    size_t buffer_pos;                  /* Byte offset of buffer position. */
+    union value *buffer;                /* I/O buffer. */
+    size_t buffer_pos;                  /* Offset of buffer position. */
     struct ccase c;                     /* Current case. */
   };
 
     struct ccase c;                     /* Current case. */
   };
 
@@ -107,8 +106,8 @@ static void fill_buffer (struct casereader *reader);
 
 static int safe_open (const char *filename, int flags);
 static int safe_close (int fd);
 
 static int safe_open (const char *filename, int flags);
 static int safe_close (int fd);
-static int full_read (int fd, char *buffer, size_t size);
-static int full_write (int fd, const char *buffer, size_t size);
+static int full_read (int fd, void *buffer, size_t size);
+static int full_write (int fd, const void *buffer, size_t size);
 
 /* Creates and returns a casefile to store cases of VALUE_CNT
    `union value's each. */
 
 /* Creates and returns a casefile to store cases of VALUE_CNT
    `union value's each. */
@@ -122,8 +121,7 @@ casefile_create (size_t value_cnt)
     cf->next->prev = cf;
   casefiles = cf;
   cf->value_cnt = value_cnt;
     cf->next->prev = cf;
   casefiles = cf;
   cf->value_cnt = value_cnt;
-  cf->case_size = case_serial_size (value_cnt);
-  cf->case_acct_size = cf->case_size + 4 * sizeof (void *);
+  cf->case_acct_size = (cf->value_cnt + 4) * sizeof *cf->buffer;
   cf->case_cnt = 0;
   cf->storage = MEMORY;
   cf->mode = WRITE;
   cf->case_cnt = 0;
   cf->storage = MEMORY;
   cf->mode = WRITE;
@@ -133,9 +131,9 @@ casefile_create (size_t value_cnt)
   cf->fd = -1;
   cf->filename = NULL;
   cf->buffer = NULL;
   cf->fd = -1;
   cf->filename = NULL;
   cf->buffer = NULL;
-  cf->buffer_size = ROUND_UP (cf->case_size, IO_BUF_SIZE);
-  if (cf->case_size > 0 && cf->buffer_size % cf->case_size > 512)
-    cf->buffer_size = cf->case_size;
+  cf->buffer_size = ROUND_UP (cf->value_cnt, IO_BUF_SIZE);
+  if (cf->value_cnt > 0 && cf->buffer_size % cf->value_cnt > 64)
+    cf->buffer_size = cf->value_cnt;
   cf->buffer_used = 0;
   register_atexit ();
   return cf;
   cf->buffer_used = 0;
   register_atexit ();
   return cf;
@@ -312,9 +310,9 @@ casefile_append_xfer (struct casefile *cf, struct ccase *c)
 static void
 write_case_to_disk (struct casefile *cf, const struct ccase *c) 
 {
 static void
 write_case_to_disk (struct casefile *cf, const struct ccase *c) 
 {
-  case_serialize (c, cf->buffer + cf->buffer_used, cf->case_size);
-  cf->buffer_used += cf->case_size;
-  if (cf->buffer_used + cf->case_size > cf->buffer_size)
+  case_to_values (c, cf->buffer + cf->buffer_used, cf->value_cnt);
+  cf->buffer_used += cf->value_cnt;
+  if (cf->buffer_used + cf->value_cnt > cf->buffer_size)
     flush_buffer (cf);
 }
 
     flush_buffer (cf);
 }
 
@@ -325,7 +323,8 @@ flush_buffer (struct casefile *cf)
 {
   if (cf->buffer_used > 0) 
     {
 {
   if (cf->buffer_used > 0) 
     {
-      if (!full_write (cf->fd, cf->buffer, cf->buffer_size)) 
+      if (!full_write (cf->fd, cf->buffer,
+                       cf->buffer_size * sizeof *cf->buffer)) 
         msg (FE, _("Error writing temporary file: %s."), strerror (errno));
 
       cf->buffer_used = 0;
         msg (FE, _("Error writing temporary file: %s."), strerror (errno));
 
       cf->buffer_used = 0;
@@ -371,7 +370,7 @@ casefile_to_disk (const struct casefile *cf_)
   struct casereader *reader;
   
   assert (cf != NULL);
   struct casereader *reader;
   
   assert (cf != NULL);
-  
+
   if (cf->storage == MEMORY)
     {
       size_t idx, block_cnt;
   if (cf->storage == MEMORY)
     {
       size_t idx, block_cnt;
@@ -383,8 +382,8 @@ casefile_to_disk (const struct casefile *cf_)
       cf->storage = DISK;
       if (!make_temp_file (&cf->fd, &cf->filename))
         err_failure ();
       cf->storage = DISK;
       if (!make_temp_file (&cf->fd, &cf->filename))
         err_failure ();
-      cf->buffer = xmalloc (cf->buffer_size);
-      memset (cf->buffer, 0, cf->buffer_size);
+      cf->buffer = xmalloc (cf->buffer_size * sizeof *cf->buffer);
+      memset (cf->buffer, 0, cf->buffer_size * sizeof *cf->buffer);
 
       case_bytes -= cf->case_cnt * cf->case_acct_size;
       for (idx = 0; idx < cf->case_cnt; idx++)
 
       case_bytes -= cf->case_cnt * cf->case_acct_size;
       for (idx = 0; idx < cf->case_cnt; idx++)
@@ -482,7 +481,6 @@ static void
 reader_open_file (struct casereader *reader) 
 {
   struct casefile *cf = reader->cf;
 reader_open_file (struct casereader *reader) 
 {
   struct casefile *cf = reader->cf;
-  size_t buffer_case_cnt;
   off_t file_ofs;
 
   if (reader->case_idx >= cf->case_cnt)
   off_t file_ofs;
 
   if (reader->case_idx >= cf->case_cnt)
@@ -508,17 +506,17 @@ reader_open_file (struct casereader *reader)
     }
   else 
     {
     }
   else 
     {
-      reader->buffer = xmalloc (cf->buffer_size);
-      memset (reader->buffer, 0, cf->buffer_size); 
+      reader->buffer = xmalloc (cf->buffer_size * sizeof *cf->buffer);
+      memset (reader->buffer, 0, cf->buffer_size * sizeof *cf->buffer); 
     }
 
     }
 
-  if (cf->case_size != 0) 
+  if (cf->value_cnt != 0) 
     {
     {
-      buffer_case_cnt = cf->buffer_size / cf->case_size;
-      file_ofs = ((off_t) reader->case_idx
-                  / buffer_case_cnt * cf->buffer_size);
+      size_t buffer_case_cnt = cf->buffer_size / cf->value_cnt;
+      file_ofs = ((off_t) reader->case_idx / buffer_case_cnt
+                  * cf->buffer_size * sizeof *cf->buffer);
       reader->buffer_pos = (reader->case_idx % buffer_case_cnt
       reader->buffer_pos = (reader->case_idx % buffer_case_cnt
-                            * cf->case_size);
+                            * cf->value_cnt);
     }
   else 
     file_ofs = 0;
     }
   else 
     file_ofs = 0;
@@ -526,7 +524,7 @@ reader_open_file (struct casereader *reader)
     msg (FE, _("%s: Seeking temporary file: %s."),
          cf->filename, strerror (errno));
 
     msg (FE, _("%s: Seeking temporary file: %s."),
          cf->filename, strerror (errno));
 
-  if (cf->case_cnt > 0 && cf->case_size > 0)
+  if (cf->case_cnt > 0 && cf->value_cnt > 0)
     fill_buffer (reader);
 
   case_create (&reader->c, cf->value_cnt);
     fill_buffer (reader);
 
   case_create (&reader->c, cf->value_cnt);
@@ -536,11 +534,12 @@ reader_open_file (struct casereader *reader)
 static void
 fill_buffer (struct casereader *reader)
 {
 static void
 fill_buffer (struct casereader *reader)
 {
-  int retval = full_read (reader->fd, reader->buffer, reader->cf->buffer_size);
+  int retval = full_read (reader->fd, reader->buffer,
+                          reader->cf->buffer_size * sizeof *reader->buffer);
   if (retval < 0)
     msg (FE, _("%s: Reading temporary file: %s."),
          reader->cf->filename, strerror (errno));
   if (retval < 0)
     msg (FE, _("%s: Reading temporary file: %s."),
          reader->cf->filename, strerror (errno));
-  else if (retval != reader->cf->buffer_size)
+  else if (retval != reader->cf->buffer_size * sizeof *reader->buffer)
     msg (FE, _("%s: Temporary file ended unexpectedly."),
          reader->cf->filename); 
 }
     msg (FE, _("%s: Temporary file ended unexpectedly."),
          reader->cf->filename); 
 }
@@ -575,15 +574,15 @@ casereader_read (struct casereader *reader, struct ccase *c)
     }
   else 
     {
     }
   else 
     {
-      if (reader->buffer_pos + reader->cf->case_size > reader->cf->buffer_size)
+      if (reader->buffer_pos + reader->cf->value_cnt > reader->cf->buffer_size)
         {
           fill_buffer (reader);
           reader->buffer_pos = 0;
         }
 
         {
           fill_buffer (reader);
           reader->buffer_pos = 0;
         }
 
-      case_unserialize (&reader->c, reader->buffer + reader->buffer_pos,
-                        reader->cf->case_size);
-      reader->buffer_pos += reader->cf->case_size;
+      case_from_values (&reader->c, reader->buffer + reader->buffer_pos,
+                        reader->cf->value_cnt);
+      reader->buffer_pos += reader->cf->value_cnt;
       reader->case_idx++;
 
       case_clone (c, &reader->c);
       reader->case_idx++;
 
       case_clone (c, &reader->c);
@@ -679,8 +678,9 @@ static int safe_close (int fd)
 /* Calls read(), passing FD, BUFFER, and SIZE, repeating as
    necessary to deal with interrupted calls. */
 static int
 /* Calls read(), passing FD, BUFFER, and SIZE, repeating as
    necessary to deal with interrupted calls. */
 static int
-full_read (int fd, char *buffer, size_t size) 
+full_read (int fd, void *buffer_, size_t size) 
 {
 {
+  char *buffer = buffer_;
   size_t bytes_read = 0;
   
   while (bytes_read < size)
   size_t bytes_read = 0;
   
   while (bytes_read < size)
@@ -700,8 +700,9 @@ full_read (int fd, char *buffer, size_t size)
 /* Calls write(), passing FD, BUFFER, and SIZE, repeating as
    necessary to deal with interrupted calls. */
 static int
 /* Calls write(), passing FD, BUFFER, and SIZE, repeating as
    necessary to deal with interrupted calls. */
 static int
-full_write (int fd, const char *buffer, size_t size) 
+full_write (int fd, const void *buffer_, size_t size) 
 {
 {
+  const char *buffer = buffer_;
   size_t bytes_written = 0;
   
   while (bytes_written < size)
   size_t bytes_written = 0;
   
   while (bytes_written < size)
@@ -830,8 +831,8 @@ test_casefile (int pattern, size_t value_cnt, size_t case_cnt)
       for (i = j = 0; i < case_cnt; i++) 
         {
           read_and_verify_random_case (cf, r1, i);
       for (i = j = 0; i < case_cnt; i++) 
         {
           read_and_verify_random_case (cf, r1, i);
-          if (rng_get_int (rng) % pattern == 0)
-            read_and_verify_random_case (cf, r2, j++);
+          if (rng_get_int (rng) % pattern == 0) 
+            read_and_verify_random_case (cf, r2, j++); 
           if (i == case_cnt / 2)
             casefile_to_disk (cf);
         }
           if (i == case_cnt / 2)
             casefile_to_disk (cf);
         }
index c550edf1ea11c5a68ec806c25444fb1e935ae7aa..34527e41628edd30adc7635181ea9b399901eac8 100644 (file)
@@ -17,7 +17,7 @@
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
    02111-1307, USA. */
 
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
    02111-1307, USA. */
 
-
+#include <config.h>
 #include <stdio.h>
 #include <plot.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <plot.h>
 #include <stdarg.h>
@@ -28,6 +28,7 @@
 #include <math.h>
 
 #include "chart.h"
 #include <math.h>
 
 #include "chart.h"
+#include "str.h"
 
 
 const char *data_colour[] = {
 
 
 const char *data_colour[] = {
index aaf7abc850437baf9e24dd283ef48ada7dfa8ffb..d4a8dd7434bdf99243a7110462c9f8f6422cecad 100644 (file)
@@ -115,6 +115,7 @@ struct normal_curve
 
 void draw_histogram(struct chart *ch, 
                    const struct variable *v,
 
 void draw_histogram(struct chart *ch, 
                    const struct variable *v,
+                    const struct freq_tab *frq_tab,
                    const char *title, 
                    struct normal_curve *norm,
                    int show_normal);
                    const char *title, 
                    struct normal_curve *norm,
                    int show_normal);
@@ -123,7 +124,8 @@ void draw_histogram(struct chart *ch,
 double chart_rounded_tick(double tick);
 
 
 double chart_rounded_tick(double tick);
 
 
-void draw_piechart(struct chart *ch, const struct variable *v);
+void draw_piechart(struct chart *ch, const struct variable *v,
+                   const struct freq_tab *);
 
 void draw_scatterplot(struct chart *ch);
 
 
 void draw_scatterplot(struct chart *ch);
 
index 5ff4f0de2c0e746cfac3c81b28cb05e9f6380cb6..5e4e1a5f7c035264a9d37836201fa0973b717119 100644 (file)
@@ -25,6 +25,7 @@
 #include <ctype.h>
 #include <errno.h>
 #include "alloc.h"
 #include <ctype.h>
 #include <errno.h>
 #include "alloc.h"
+#include "dictionary.h"
 #include "error.h"
 #include "getline.h"
 #include "lexer.h"
 #include "error.h"
 #include "getline.h"
 #include "lexer.h"
index f30d7b1f64c281bb56f65b3faa7a6b330266167b..6556e8becac0041d63303556817f6f203f4fd09d 100644 (file)
@@ -23,6 +23,7 @@
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "expr.h"
 #include "lexer.h"
 #include "error.h"
 #include "expr.h"
 #include "lexer.h"
index 4c9f74b7f3344f5a5e32fdcbe70045bf679a9c45..4bde9a06311954a2848125b54d015d4ae08216d4 100644 (file)
@@ -20,6 +20,7 @@
 #include <config.h>
 #include <stdlib.h>
 #include "alloc.h"
 #include <config.h>
 #include <stdlib.h>
 #include "alloc.h"
+#include "dictionary.h"
 #include "file-handle.h"
 #include "command.h"
 #include "lexer.h"
 #include "file-handle.h"
 #include "command.h"
 #include "lexer.h"
@@ -133,12 +134,13 @@ cor_custom_matrix (struct cmd_correlations *cmd UNUSED)
     return 0;
   
   if (lex_match ('*'))
     return 0;
   
   if (lex_match ('*'))
-    matrix_file = inline_file;
-  else
-    matrix_file = fh_parse_file_handle ();
-
-  if (!matrix_file)
-    return 0;
+    matrix_file = NULL;
+  else 
+    {
+      matrix_file = fh_parse ();
+      if (matrix_file == NULL)
+        return 0; 
+    }
 
   if (!lex_force_match (')'))
     return 0;
 
   if (!lex_force_match (')'))
     return 0;
index a6e70616460cf7ce40583c0f858e5801095d75a7..35021f09e3589d8055c240d528711dcb0ef73d5f 100644 (file)
@@ -23,6 +23,7 @@
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "lexer.h"
 #include "str.h"
 #include "error.h"
 #include "lexer.h"
 #include "str.h"
index 0a1e891f2470228ff5c53375f3f58a7cc76a3d39..9c17ed0297379c06887ada4803d55de8bad91543 100644 (file)
@@ -38,6 +38,7 @@
 #include "algorithm.h"
 #include "alloc.h"
 #include "case.h"
 #include "algorithm.h"
 #include "alloc.h"
 #include "case.h"
+#include "dictionary.h"
 #include "hash.h"
 #include "pool.h"
 #include "command.h"
 #include "hash.h"
 #include "pool.h"
 #include "command.h"
 #include "magic.h"
 #include "misc.h"
 #include "output.h"
 #include "magic.h"
 #include "misc.h"
 #include "output.h"
+#include "str.h"
 #include "tab.h"
 #include "value-labels.h"
 #include "var.h"
 #include "vfm.h"
 
 #include "tab.h"
 #include "value-labels.h"
 #include "var.h"
 #include "vfm.h"
 
+/* (headers) */
+
 #include "debug-print.h"
 
 /* (specification)
 #include "debug-print.h"
 
 /* (specification)
@@ -105,6 +109,22 @@ struct crosstab
                                   larger indices first. */
   };
 
                                   larger indices first. */
   };
 
+/* Integer mode variable info. */
+struct var_range
+  {
+    int min;                   /* Minimum value. */
+    int max;                   /* Maximum value + 1. */
+    int count;                 /* max - min. */
+  };
+
+static inline struct var_range *
+get_var_range (struct variable *v) 
+{
+  assert (v != NULL);
+  assert (v->aux != NULL);
+  return v->aux;
+}
+
 /* Indexes into crosstab.v. */
 enum
   {
 /* Indexes into crosstab.v. */
 enum
   {
@@ -426,11 +446,13 @@ crs_custom_variables (struct cmd_crosstabs *cmd UNUSED)
        }
       lex_get ();
       
        }
       lex_get ();
       
-      for (i = orig_nv; i < variables_cnt; i++)
-       {
-         variables[i]->p.crs.min = min;
-         variables[i]->p.crs.max = max + 1.;
-         variables[i]->p.crs.count = max - min + 1;
+      for (i = orig_nv; i < variables_cnt; i++) 
+        {
+          struct var_range *vr = xmalloc (sizeof *vr);
+          vr->min = min;
+         vr->max = max + 1.;
+         vr->count = max - min + 1;
+          var_attach_aux (variables[i], vr, var_dtor_free);
        }
       
       if (token == '/')
        }
       
       if (token == '/')
@@ -475,14 +497,14 @@ precalc (void *aux UNUSED)
 
          x->ofs = n_sorted_tab;
 
 
          x->ofs = n_sorted_tab;
 
-         for (j = 2; j < x->nvar; j++)
-           count *= x->vars[j - 2]->p.crs.count;
-
+         for (j = 2; j < x->nvar; j++) 
+            count *= get_var_range (x->vars[j - 2])->count;
+          
          sorted_tab = xrealloc (sorted_tab,
                                 sizeof *sorted_tab * (n_sorted_tab + count));
          v = local_alloc (sizeof *v * x->nvar);
          sorted_tab = xrealloc (sorted_tab,
                                 sizeof *sorted_tab * (n_sorted_tab + count));
          v = local_alloc (sizeof *v * x->nvar);
-         for (j = 2; j < x->nvar; j++)
-           v[j] = x->vars[j]->p.crs.min;
+         for (j = 2; j < x->nvar; j++) 
+            v[j] = get_var_range (x->vars[j])->min; 
          for (j = 0; j < count; j++)
            {
              struct table_entry *te;
          for (j = 0; j < count; j++)
            {
              struct table_entry *te;
@@ -493,8 +515,9 @@ precalc (void *aux UNUSED)
              te->table = i;
              
              {
              te->table = i;
              
              {
-               const int mat_size = (x->vars[0]->p.crs.count
-                                     * x->vars[1]->p.crs.count);
+                int row_cnt = get_var_range (x->vars[0])->count;
+                int col_cnt = get_var_range (x->vars[1])->count;
+               const int mat_size = row_cnt * col_cnt;
                int m;
                
                te->u.data = xmalloc (sizeof *te->u.data * mat_size);
                int m;
                
                te->u.data = xmalloc (sizeof *te->u.data * mat_size);
@@ -504,11 +527,14 @@ precalc (void *aux UNUSED)
              
              for (k = 2; k < x->nvar; k++)
                te->values[k].f = v[k];
              
              for (k = 2; k < x->nvar; k++)
                te->values[k].f = v[k];
-             for (k = 2; k < x->nvar; k++)
-               if (++v[k] >= x->vars[k]->p.crs.max)
-                 v[k] = x->vars[k]->p.crs.min;
-               else
-                 break;
+             for (k = 2; k < x->nvar; k++) 
+                {
+                  struct var_range *vr = get_var_range (x->vars[k]);
+                  if (++v[k] >= vr->max)
+                    v[k] = vr->min;
+                  else
+                    break; 
+                }
            }
          local_free (v);
        }
            }
          local_free (v);
        }
@@ -615,10 +641,11 @@ calc_integer (struct ccase *c, void *aux UNUSED)
       for (i = 0; i < x->nvar; i++)
        {
          struct variable *const v = x->vars[i];
       for (i = 0; i < x->nvar; i++)
        {
          struct variable *const v = x->vars[i];
+          struct var_range *vr = get_var_range (v);
          double value = case_num (c, v->fv);
          
          /* Note that the first test also rules out SYSMIS. */
          double value = case_num (c, v->fv);
          
          /* Note that the first test also rules out SYSMIS. */
-         if ((value < v->p.crs.min || value >= v->p.crs.max)
+         if ((value < vr->min || value >= vr->max)
              || (cmd.miss == CRS_TABLE && is_num_user_missing (value, v)))
            {
              x->missing += weight;
              || (cmd.miss == CRS_TABLE && is_num_user_missing (value, v)))
            {
              x->missing += weight;
@@ -627,15 +654,19 @@ calc_integer (struct ccase *c, void *aux UNUSED)
          
          if (i > 1)
            {
          
          if (i > 1)
            {
-             ofs += fact * ((int) value - v->p.crs.min);
-             fact *= v->p.crs.count;
+             ofs += fact * ((int) value - vr->min);
+             fact *= vr->count;
            }
        }
       
       {
            }
        }
       
       {
-       const int row = case_num (c, x->vars[ROW_VAR]->fv) - x->vars[ROW_VAR]->p.crs.min;
-       const int col = case_num (c, x->vars[COL_VAR]->fv) - x->vars[COL_VAR]->p.crs.min;
-       const int col_dim = x->vars[COL_VAR]->p.crs.count;
+        struct variable *row_var = x->vars[ROW_VAR];
+       const int row = case_num (c, row_var->fv) - get_var_range (row_var)->min;
+
+        struct variable *col_var = x->vars[COL_VAR];
+       const int col = case_num (c, col_var->fv) - get_var_range (col_var)->min;
+
+       const int col_dim = get_var_range (col_var)->count;
 
        sorted_tab[ofs]->u.data[col + row * col_dim] += weight;
       }
 
        sorted_tab[ofs]->u.data[col + row * col_dim] += weight;
       }
@@ -804,8 +835,8 @@ make_summary_table (void)
       else
        {
          const struct crosstab *const x = xtab[(*pb)->table];
       else
        {
          const struct crosstab *const x = xtab[(*pb)->table];
-         const int n_cols = x->vars[COL_VAR]->p.crs.count;
-         const int n_rows = x->vars[ROW_VAR]->p.crs.count;
+         const int n_cols = get_var_range (x->vars[COL_VAR])->count;
+         const int n_rows = get_var_range (x->vars[ROW_VAR])->count;
          const int count = n_cols * n_rows;
            
          for (valid = 0.; pb < pe; pb++)
          const int count = n_cols * n_rows;
            
          for (valid = 0.; pb < pe; pb++)
@@ -1569,7 +1600,7 @@ compare_value (const void *a_, const void *b_, void *width_)
 
 /* Given an array of ENTRY_CNT table_entry structures starting at
    ENTRIES, creates a sorted list of the values that the variable
 
 /* Given an array of ENTRY_CNT table_entry structures starting at
    ENTRIES, creates a sorted list of the values that the variable
-   with index VAR_INDEX takes on.  The values are returned as a
+   with index VAR_IDX takes on.  The values are returned as a
    malloc()'darray stored in *VALUES, with the number of values
    stored in *VALUE_CNT.
    */
    malloc()'darray stored in *VALUES, with the number of values
    stored in *VALUE_CNT.
    */
@@ -1577,9 +1608,11 @@ static void
 enum_var_values (struct table_entry **entries, int entry_cnt, int var_idx,
                  union value **values, int *value_cnt)
 {
 enum_var_values (struct table_entry **entries, int entry_cnt, int var_idx,
                  union value **values, int *value_cnt)
 {
+  struct variable *v = xtab[(*entries)->table]->vars[var_idx];
+
   if (mode == GENERAL)
     {
   if (mode == GENERAL)
     {
-      int width = xtab[(*entries)->table]->vars[var_idx]->width;
+      int width = v->width;
       int i;
 
       *values = xmalloc (sizeof **values * entry_cnt);
       int i;
 
       *values = xmalloc (sizeof **values * entry_cnt);
@@ -1590,15 +1623,14 @@ enum_var_values (struct table_entry **entries, int entry_cnt, int var_idx,
     }
   else
     {
     }
   else
     {
-      struct crosstab_proc *crs
-        = &xtab[(*entries)->table]->vars[var_idx]->p.crs;
+      struct var_range *vr = get_var_range (v);
       int i;
       
       assert (mode == INTEGER);
       int i;
       
       assert (mode == INTEGER);
-      *values = xmalloc (sizeof **values * crs->count);
-      for (i = 0; i < crs->count; i++)
-       (*values)[i].f = i + crs->min;
-      *value_cnt = crs->count;
+      *values = xmalloc (sizeof **values * vr->count);
+      for (i = 0; i < vr->count; i++)
+       (*values)[i].f = i + vr->min;
+      *value_cnt = vr->count;
     }
 }
 
     }
 }
 
index a3e6c67a2ad251251b326b63df68ccc21d0f7be8..7328f65eec9781bc5814ca010cc4ae11ba059012 100644 (file)
@@ -29,7 +29,8 @@
 #include "command.h"
 #include "data-in.h"
 #include "debug-print.h"
 #include "command.h"
 #include "data-in.h"
 #include "debug-print.h"
-#include "dfm.h"
+#include "dfm-read.h"
+#include "dictionary.h"
 #include "error.h"
 #include "file-handle.h"
 #include "format.h"
 #include "error.h"
 #include "file-handle.h"
 #include "format.h"
@@ -82,12 +83,12 @@ struct data_list_pgm
     struct trns_header h;
 
     struct dls_var_spec *first, *last; /* Variable parsing specifications. */
     struct trns_header h;
 
     struct dls_var_spec *first, *last; /* Variable parsing specifications. */
-    struct file_handle *handle;        /* Input file, never NULL. */
+    struct dfm_reader *reader;  /* Data file reader. */
 
     int type;                  /* A DLS_* constant. */
     struct variable *end;      /* Variable specified on END subcommand. */
     int eof;                   /* End of file encountered. */
 
     int type;                  /* A DLS_* constant. */
     struct variable *end;      /* Variable specified on END subcommand. */
     int eof;                   /* End of file encountered. */
-    int nrec;                  /* Number of records. */
+    int rec_cnt;                /* Number of records. */
     size_t case_size;           /* Case size in bytes. */
     char *delims;               /* Delimiters if any; not null-terminated. */
     size_t delim_cnt;           /* Number of delimiter, or 0 for spaces. */
     size_t case_size;           /* Case size in bytes. */
     char *delims;               /* Delimiters if any; not null-terminated. */
     size_t delim_cnt;           /* Number of delimiter, or 0 for spaces. */
@@ -95,9 +96,10 @@ struct data_list_pgm
 
 static int parse_fixed (struct data_list_pgm *);
 static int parse_free (struct dls_var_spec **, struct dls_var_spec **);
 
 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 *specs,
-                              const struct file_handle *handle, int nrec);
-static void dump_free_table (const struct data_list_pgm *);
+static void dump_fixed_table (const struct dls_var_spec *,
+                              const struct file_handle *, int rec_cnt);
+static void dump_free_table (const struct data_list_pgm *,
+                             const struct file_handle *);
 static void destroy_dls_var_spec (struct dls_var_spec *);
 static trns_free_func data_list_trns_free;
 static trns_proc_func data_list_trns_proc;
 static void destroy_dls_var_spec (struct dls_var_spec *);
 static trns_free_func data_list_trns_free;
 static trns_proc_func data_list_trns_proc;
@@ -108,21 +110,19 @@ static trns_proc_func data_list_trns_proc;
 int
 cmd_data_list (void)
 {
 int
 cmd_data_list (void)
 {
-  /* DATA LIST program under construction. */
-  struct data_list_pgm *dls;
-
-  /* 0=print no table, 1=print table.  (TABLE subcommand.)  */
-  int table = -1;
+  struct data_list_pgm *dls;     /* DATA LIST program under construction. */
+  int table = -1;                /* Print table if nonzero, -1=undecided. */
+  struct file_handle *fh = NULL; /* File handle of source, NULL=inline file. */
 
   if (!case_source_is_complex (vfm_source))
     discard_variables ();
 
   dls = xmalloc (sizeof *dls);
 
   if (!case_source_is_complex (vfm_source))
     discard_variables ();
 
   dls = xmalloc (sizeof *dls);
-  dls->handle = default_handle;
+  dls->reader = NULL;
   dls->type = -1;
   dls->end = NULL;
   dls->eof = 0;
   dls->type = -1;
   dls->end = NULL;
   dls->eof = 0;
-  dls->nrec = 0;
+  dls->rec_cnt = 0;
   dls->delims = NULL;
   dls->delim_cnt = 0;
   dls->first = dls->last = NULL;
   dls->delims = NULL;
   dls->delim_cnt = 0;
   dls->first = dls->last = NULL;
@@ -132,11 +132,11 @@ cmd_data_list (void)
       if (lex_match_id ("FILE"))
        {
          lex_match ('=');
       if (lex_match_id ("FILE"))
        {
          lex_match ('=');
-         dls->handle = fh_parse_file_handle ();
-         if (!dls->handle)
+         fh = fh_parse ();
+         if (fh == NULL)
            goto error;
          if (case_source_is_class (vfm_source, &file_type_source_class)
            goto error;
          if (case_source_is_class (vfm_source, &file_type_source_class)
-              && dls->handle != default_handle)
+              && fh != default_handle)
            {
              msg (SE, _("DATA LIST may not use a different file from "
                         "that specified on its surrounding FILE TYPE."));
            {
              msg (SE, _("DATA LIST may not use a different file from "
                         "that specified on its surrounding FILE TYPE."));
@@ -149,7 +149,7 @@ cmd_data_list (void)
          lex_match ('(');
          if (!lex_force_int ())
            goto error;
          lex_match ('(');
          if (!lex_force_int ())
            goto error;
-         dls->nrec = lex_integer ();
+         dls->rec_cnt = lex_integer ();
          lex_get ();
          lex_match (')');
        }
          lex_get ();
          lex_match (')');
        }
@@ -231,7 +231,7 @@ cmd_data_list (void)
     }
 
   dls->case_size = dict_get_case_size (default_dict);
     }
 
   dls->case_size = dict_get_case_size (default_dict);
-  default_handle = dls->handle;
+  default_handle = fh;
 
   if (dls->type == -1)
     dls->type = DLS_FIXED;
 
   if (dls->type == -1)
     dls->type = DLS_FIXED;
@@ -249,17 +249,18 @@ cmd_data_list (void)
       if (!parse_fixed (dls))
        goto error;
       if (table)
       if (!parse_fixed (dls))
        goto error;
       if (table)
-       dump_fixed_table (dls->first, dls->handle, dls->nrec);
+       dump_fixed_table (dls->first, fh, dls->rec_cnt);
     }
   else
     {
       if (!parse_free (&dls->first, &dls->last))
        goto error;
       if (table)
     }
   else
     {
       if (!parse_free (&dls->first, &dls->last))
        goto error;
       if (table)
-       dump_free_table (dls);
+       dump_free_table (dls, fh);
     }
 
     }
 
-  if (!dfm_open_for_reading (dls->handle))
+  dls->reader = dfm_open_reader (fh);
+  if (dls->reader == NULL)
     goto error;
 
   if (vfm_source != NULL)
     goto error;
 
   if (vfm_source != NULL)
@@ -393,14 +394,14 @@ parse_fixed (struct data_list_pgm *dls)
       msg (SE, _("At least one variable must be specified."));
       return 0;
     }
       msg (SE, _("At least one variable must be specified."));
       return 0;
     }
-  if (dls->nrec && dls->last->rec > dls->nrec)
+  if (dls->rec_cnt && dls->last->rec > dls->rec_cnt)
     {
       msg (SE, _("Variables are specified on records that "
                 "should not exist according to RECORDS subcommand."));
       return 0;
     }
     {
       msg (SE, _("Variables are specified on records that "
                 "should not exist according to RECORDS subcommand."));
       return 0;
     }
-  else if (!dls->nrec)
-    dls->nrec = dls->last->rec;
+  else if (!dls->rec_cnt)
+    dls->rec_cnt = dls->last->rec;
   if (token != '.')
     {
       lex_error (_("expecting end of command"));
   if (token != '.')
     {
       lex_error (_("expecting end of command"));
@@ -778,12 +779,10 @@ fixed_parse_fortran (struct fixed_parsing_state *fx,
    ending column. */
 static void
 dump_fixed_table (const struct dls_var_spec *specs,
    ending column. */
 static void
 dump_fixed_table (const struct dls_var_spec *specs,
-                  const struct file_handle *handle, int nrec)
+                  const struct file_handle *fh, int rec_cnt)
 {
   const struct dls_var_spec *spec;
   struct tab_table *t;
 {
   const struct dls_var_spec *spec;
   struct tab_table *t;
-  char *buf;
-  const char *filename;
   int i;
 
   for (i = 0, spec = specs; spec; spec = spec->next)
   int i;
 
   for (i = 0, spec = specs; spec; spec = spec->next)
@@ -809,21 +808,16 @@ dump_fixed_table (const struct dls_var_spec *specs,
                    fmt_to_string (&spec->input));
     }
 
                    fmt_to_string (&spec->input));
     }
 
-  filename = handle_get_filename (handle);
-  if (filename == NULL)
-    filename = "";
-  buf = local_alloc (strlen (filename) + INT_DIGITS + 80);
-  sprintf (buf, (handle != inline_file
-                 ? ngettext ("Reading %d record from file %s.",
-                             "Reading %d records from file %s.", nrec)
-                 : ngettext ("Reading %d record from the command file.",
-                             "Reading %d records from the command file.",
-                             nrec)),
-           nrec, filename);
-  
-  tab_title (t, 0, buf);
+  if (fh != NULL)
+    tab_title (t, 1, ngettext ("Reading %d record from file %s.",
+                               "Reading %d records from file %s.", rec_cnt),
+               rec_cnt, handle_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_submit (t);
   tab_submit (t);
-  local_free (buf);
 }
 \f
 /* Free-format parsing. */
 }
 \f
 /* Free-format parsing. */
@@ -907,7 +901,8 @@ parse_free (struct dls_var_spec **first, struct dls_var_spec **last)
 /* Displays a table giving information on free-format variable parsing
    on DATA LIST. */
 static void
 /* Displays a table giving information on free-format variable parsing
    on DATA LIST. */
 static void
-dump_free_table (const struct data_list_pgm *dls)
+dump_free_table (const struct data_list_pgm *dls,
+                 const struct file_handle *fh)
 {
   struct tab_table *t;
   int i;
 {
   struct tab_table *t;
   int i;
@@ -936,19 +931,12 @@ dump_free_table (const struct data_list_pgm *dls)
        tab_text (t, 1, i, TAB_LEFT | TAT_FIX, fmt_to_string (&spec->input));
       }
   }
        tab_text (t, 1, i, TAB_LEFT | TAT_FIX, fmt_to_string (&spec->input));
       }
   }
-  
-  {
-    const char *filename;
-
-    filename = handle_get_filename (dls->handle);
-    if (filename == NULL)
-      filename = "";
-    tab_title (t, 1,
-              (dls->handle != inline_file
-               ? _("Reading free-form data from file %s.")
-               : _("Reading free-form data from the command file.")),
-              filename);
-  }
+
+  if (fh != NULL)
+    tab_title (t, 1, _("Reading free-form data from file %s."),
+               handle_get_filename (fh));
+  else
+    tab_title (t, 1, _("Reading free-form data from the command file."));
   
   tab_submit (t);
 }
   
   tab_submit (t);
 }
@@ -972,11 +960,11 @@ cut_field (const struct data_list_pgm *dls, struct len_string *field,
   char *cp;
   size_t column_start;
 
   char *cp;
   size_t column_start;
 
-  if (dfm_eof (dls->handle))
+  if (dfm_eof (dls->reader))
     return 0;
   if (dls->delim_cnt == 0)
     return 0;
   if (dls->delim_cnt == 0)
-    dfm_expand_tabs (dls->handle);
-  dfm_get_record (dls->handle, &line);
+    dfm_expand_tabs (dls->reader);
+  dfm_get_record (dls->reader, &line);
 
   cp = ls_c_str (&line);
   if (dls->delim_cnt == 0) 
 
   cp = ls_c_str (&line);
   if (dls->delim_cnt == 0) 
@@ -1020,7 +1008,7 @@ cut_field (const struct data_list_pgm *dls, struct len_string *field,
     {
       if (cp >= ls_end (&line)) 
         {
     {
       if (cp >= ls_end (&line)) 
         {
-          int column = dfm_column_start (dls->handle);
+          int column = dfm_column_start (dls->reader);
                /* A blank line or a line that ends in \t has a
              trailing blank field. */
           if (column == 1 || (column > 1 && cp[-1] == '\t'))
                /* A blank line or a line that ends in \t has a
              trailing blank field. */
           if (column == 1 || (column > 1 && cp[-1] == '\t'))
@@ -1030,7 +1018,7 @@ cut_field (const struct data_list_pgm *dls, struct len_string *field,
                   *end_blank = 1;
                   field->string = ls_end (&line);
                   field->length = 0;
                   *end_blank = 1;
                   field->string = ls_end (&line);
                   field->length = 0;
-                  dfm_forward_record (dls->handle);
+                  dfm_forward_record (dls->reader);
                   return column;
                 }
               else 
                   return column;
                 }
               else 
@@ -1054,10 +1042,10 @@ cut_field (const struct data_list_pgm *dls, struct len_string *field,
         }
     }
   
         }
     }
   
-  dfm_forward_columns (dls->handle, field->string - line.string);
-  column_start = dfm_column_start (dls->handle);
+  dfm_forward_columns (dls->reader, field->string - line.string);
+  column_start = dfm_column_start (dls->reader);
     
     
-  dfm_forward_columns (dls->handle, cp - field->string);
+  dfm_forward_columns (dls->reader, cp - field->string);
     
   return column_start;
 }
     
   return column_start;
 }
@@ -1099,21 +1087,21 @@ read_from_data_list_fixed (const struct data_list_pgm *dls,
   struct dls_var_spec *var_spec = dls->first;
   int i;
 
   struct dls_var_spec *var_spec = dls->first;
   int i;
 
-  if (dfm_eof (dls->handle))
+  if (dfm_eof (dls->reader))
     return -2;
     return -2;
-  for (i = 1; i <= dls->nrec; i++)
+  for (i = 1; i <= dls->rec_cnt; i++)
     {
       struct len_string line;
       
     {
       struct len_string line;
       
-      if (dfm_eof (dls->handle))
+      if (dfm_eof (dls->reader))
        {
          /* Note that this can't occur on the first record. */
          msg (SW, _("Partial case of %d of %d records discarded."),
        {
          /* Note that this can't occur on the first record. */
          msg (SW, _("Partial case of %d of %d records discarded."),
-              i - 1, dls->nrec);
+              i - 1, dls->rec_cnt);
          return -2;
        }
          return -2;
        }
-      dfm_expand_tabs (dls->handle);
-      dfm_get_record (dls->handle, &line);
+      dfm_expand_tabs (dls->reader);
+      dfm_get_record (dls->reader, &line);
 
       for (; var_spec && i == var_spec->rec; var_spec = var_spec->next)
        {
 
       for (; var_spec && i == var_spec->rec; var_spec = var_spec->next)
        {
@@ -1129,7 +1117,7 @@ read_from_data_list_fixed (const struct data_list_pgm *dls,
          data_in (&di);
        }
 
          data_in (&di);
        }
 
-      dfm_forward_record (dls->handle);
+      dfm_forward_record (dls->reader);
     }
 
   return -1;
     }
 
   return -1;
@@ -1157,9 +1145,9 @@ read_from_data_list_free (const struct data_list_pgm *dls,
          if (column != 0)
            break;
 
          if (column != 0)
            break;
 
-         if (!dfm_eof (dls->handle)) 
-            dfm_forward_record (dls->handle);
-         if (dfm_eof (dls->handle))
+         if (!dfm_eof (dls->reader)) 
+            dfm_forward_record (dls->reader);
+         if (dfm_eof (dls->reader))
            {
              if (var_spec != dls->first)
                msg (SW, _("Partial case discarded.  The first variable "
            {
              if (var_spec != dls->first)
                msg (SW, _("Partial case discarded.  The first variable "
@@ -1193,7 +1181,7 @@ read_from_data_list_list (const struct data_list_pgm *dls,
   struct dls_var_spec *var_spec;
   int end_blank = 0;
 
   struct dls_var_spec *var_spec;
   int end_blank = 0;
 
-  if (dfm_eof (dls->handle))
+  if (dfm_eof (dls->reader))
     return -2;
 
   for (var_spec = dls->first; var_spec; var_spec = var_spec->next)
     return -2;
 
   for (var_spec = dls->first; var_spec; var_spec = var_spec->next)
@@ -1234,7 +1222,7 @@ read_from_data_list_list (const struct data_list_pgm *dls,
       }
     }
 
       }
     }
 
-  dfm_forward_record (dls->handle);
+  dfm_forward_record (dls->reader);
   return -1;
 }
 
   return -1;
 }
 
@@ -1259,7 +1247,7 @@ data_list_trns_free (struct trns_header *pgm)
   struct data_list_pgm *dls = (struct data_list_pgm *) pgm;
   free (dls->delims);
   destroy_dls_var_spec (dls->first);
   struct data_list_pgm *dls = (struct data_list_pgm *) pgm;
   free (dls->delims);
   destroy_dls_var_spec (dls->first);
-  fh_close_handle (dls->handle);
+  dfm_close_reader (dls->reader);
   free (pgm);
 }
 
   free (pgm);
 }
 
@@ -1272,7 +1260,7 @@ data_list_trns_proc (struct trns_header *t, struct ccase *c,
   data_list_read_func *read_func;
   int retval;
 
   data_list_read_func *read_func;
   int retval;
 
-  dfm_push (dls->handle);
+  dfm_push (dls->reader);
 
   read_func = get_data_list_read_func (dls);
   retval = read_func (dls, c);
 
   read_func = get_data_list_read_func (dls);
   retval = read_func (dls, c);
@@ -1286,7 +1274,7 @@ data_list_trns_proc (struct trns_header *t, struct ccase *c,
         {
           msg (SE, _("Attempt to read past end of file."));
           err_failure ();
         {
           msg (SE, _("Attempt to read past end of file."));
           err_failure ();
-          dfm_pop (dls->handle);
+          dfm_pop (dls->reader);
           return -2;
         }
 
           return -2;
         }
 
@@ -1308,7 +1296,7 @@ data_list_trns_proc (struct trns_header *t, struct ccase *c,
         case_data_rw (c, dls->end->fv)->f = 0.0;
     }
   
         case_data_rw (c, dls->end->fv)->f = 0.0;
     }
   
-  dfm_pop (dls->handle);
+  dfm_pop (dls->reader);
 
   return retval;
 }
 
   return retval;
 }
@@ -1323,13 +1311,11 @@ data_list_source_read (struct case_source *source,
   struct data_list_pgm *dls = source->aux;
   data_list_read_func *read_func = get_data_list_read_func (dls);
 
   struct data_list_pgm *dls = source->aux;
   data_list_read_func *read_func = get_data_list_read_func (dls);
 
-  dfm_push (dls->handle);
+  dfm_push (dls->reader);
   while (read_func (dls, c) != -2)
     if (!write_case (wc_data))
       break;
   while (read_func (dls, c) != -2)
     if (!write_case (wc_data))
       break;
-  dfm_pop (dls->handle);
-
-  fh_close_handle (dls->handle);
+  dfm_pop (dls->reader);
 }
 
 /* Destroys the source's internal data. */
 }
 
 /* Destroys the source's internal data. */
@@ -1361,7 +1347,7 @@ struct repeating_data_trns
   {
     struct trns_header h;
     struct dls_var_spec *first, *last; /* Variable parsing specifications. */
   {
     struct trns_header h;
     struct dls_var_spec *first, *last; /* Variable parsing specifications. */
-    struct file_handle *handle;        /* Input file, never NULL. */
+    struct dfm_reader *reader;         /* Input file, never NULL. */
 
     struct rpd_num_or_var starts_beg;  /* STARTS=, before the dash. */
     struct rpd_num_or_var starts_end;  /* STARTS=, after the dash. */
 
     struct rpd_num_or_var starts_beg;  /* STARTS=, before the dash. */
     struct rpd_num_or_var starts_end;  /* STARTS=, after the dash. */
@@ -1392,17 +1378,14 @@ int
 cmd_repeating_data (void)
 {
   struct repeating_data_trns *rpd;
 cmd_repeating_data (void)
 {
   struct repeating_data_trns *rpd;
-
-  /* 0=print no table, 1=print table.  (TABLE subcommand.)  */
-  int table = 1;
-
-  /* Bits are set when a particular subcommand has been seen. */
-  unsigned seen = 0;
+  int table = 1;                /* Print table? */
+  unsigned seen = 0;            /* Mark subcommands as already seen. */
+  struct file_handle *const fh = default_handle;
   
   assert (case_source_is_complex (vfm_source));
 
   rpd = xmalloc (sizeof *rpd);
   
   assert (case_source_is_complex (vfm_source));
 
   rpd = xmalloc (sizeof *rpd);
-  rpd->handle = default_handle;
+  rpd->reader = dfm_open_reader (default_handle);
   rpd->first = rpd->last = NULL;
   rpd->starts_beg.num = 0;
   rpd->starts_beg.var = NULL;
   rpd->first = rpd->last = NULL;
   rpd->starts_beg.num = 0;
   rpd->starts_beg.var = NULL;
@@ -1418,11 +1401,12 @@ cmd_repeating_data (void)
     {
       if (lex_match_id ("FILE"))
        {
     {
       if (lex_match_id ("FILE"))
        {
+          struct file_handle *file;
          lex_match ('=');
          lex_match ('=');
-         rpd->handle = fh_parse_file_handle ();
-         if (!rpd->handle)
+         file = fh_parse ();
+         if (file == NULL)
            goto error;
            goto error;
-         if (rpd->handle != default_handle)
+         if (file != fh)
            {
              msg (SE, _("REPEATING DATA must use the same file as its "
                         "corresponding DATA LIST or FILE TYPE."));
            {
              msg (SE, _("REPEATING DATA must use the same file as its "
                         "corresponding DATA LIST or FILE TYPE."));
@@ -1613,9 +1597,9 @@ cmd_repeating_data (void)
 
   /* Calculate starts_end, cont_end if necessary. */
   if (rpd->starts_end.num == 0 && rpd->starts_end.var == NULL)
 
   /* Calculate starts_end, cont_end if necessary. */
   if (rpd->starts_end.num == 0 && rpd->starts_end.var == NULL)
-    rpd->starts_end.num = handle_get_record_width (rpd->handle);
+    rpd->starts_end.num = handle_get_record_width (fh);
   if (rpd->cont_end.num == 0 && rpd->starts_end.var == NULL)
   if (rpd->cont_end.num == 0 && rpd->starts_end.var == NULL)
-    rpd->cont_end.num = handle_get_record_width (rpd->handle);
+    rpd->cont_end.num = handle_get_record_width (fh);
       
   /* Calculate length if possible. */
   if ((seen & 4) == 0)
       
   /* Calculate length if possible. */
   if ((seen & 4) == 0)
@@ -1635,7 +1619,7 @@ cmd_repeating_data (void)
     goto error;
 
   if (table)
     goto error;
 
   if (table)
-    dump_fixed_table (rpd->first, rpd->handle, rpd->last->rec);
+    dump_fixed_table (rpd->first, fh, rpd->last->rec);
 
   {
     struct repeating_data_trns *new_trns;
 
   {
     struct repeating_data_trns *new_trns;
@@ -1939,15 +1923,15 @@ repeating_data_trns_proc (struct trns_header *trns, struct ccase *c,
     
   int skip_first_record = 0;
     
     
   int skip_first_record = 0;
     
-  dfm_push (t->handle);
+  dfm_push (t->reader);
   
   /* Read the current record. */
   
   /* Read the current record. */
-  dfm_reread_record (t->handle, 1);
-  dfm_expand_tabs (t->handle);
-  if (dfm_eof (t->handle))
+  dfm_reread_record (t->reader, 1);
+  dfm_expand_tabs (t->reader);
+  if (dfm_eof (t->reader))
     return -2;
     return -2;
-  dfm_get_record (t->handle, &line);
-  dfm_forward_record (t->handle);
+  dfm_get_record (t->reader, &line);
+  dfm_forward_record (t->reader);
 
   /* Calculate occurs, length. */
   occurs_left = occurs = realize_value (&t->occurs, c);
 
   /* Calculate occurs, length. */
   occurs_left = occurs = realize_value (&t->occurs, c);
@@ -2037,7 +2021,7 @@ repeating_data_trns_proc (struct trns_header *trns, struct ccase *c,
       assert (occurs_left >= 0);
 
       /* Read in another record. */
       assert (occurs_left >= 0);
 
       /* Read in another record. */
-      if (dfm_eof (t->handle))
+      if (dfm_eof (t->reader))
         {
           tmsg (SE, RPD_ERR,
                 _("Unexpected end of file with %d repetitions "
         {
           tmsg (SE, RPD_ERR,
                 _("Unexpected end of file with %d repetitions "
@@ -2045,9 +2029,9 @@ repeating_data_trns_proc (struct trns_header *trns, struct ccase *c,
                 occurs_left, occurs);
           return -2;
         }
                 occurs_left, occurs);
           return -2;
         }
-      dfm_expand_tabs (t->handle);
-      dfm_get_record (t->handle, &line);
-      dfm_forward_record (t->handle);
+      dfm_expand_tabs (t->reader);
+      dfm_get_record (t->reader, &line);
+      dfm_forward_record (t->reader);
 
       /* Parse this record. */
       info.trns = t;
 
       /* Parse this record. */
       info.trns = t;
@@ -2065,7 +2049,7 @@ repeating_data_trns_proc (struct trns_header *trns, struct ccase *c,
       occurs_left -= code;
     }
     
       occurs_left -= code;
     }
     
-  dfm_pop (t->handle);
+  dfm_pop (t->reader);
 
   /* FIXME: This is a kluge until we've implemented multiplexing of
      transformations. */
 
   /* FIXME: This is a kluge until we've implemented multiplexing of
      transformations. */
@@ -2079,7 +2063,7 @@ repeating_data_trns_free (struct trns_header *rpd_)
   struct repeating_data_trns *rpd = (struct repeating_data_trns *) rpd_;
 
   destroy_dls_var_spec (rpd->first);
   struct repeating_data_trns *rpd = (struct repeating_data_trns *) rpd_;
 
   destroy_dls_var_spec (rpd->first);
-  fh_close_handle (rpd->handle);
+  dfm_close_reader (rpd->reader);
   free (rpd->id_value);
 }
 
   free (rpd->id_value);
 }
 
index e05c92921614c4e3b11d8499248ad281fbda65ee..e84733acb02380eb33d73dc04b9834951dadf1ae 100644 (file)
@@ -29,6 +29,7 @@
 #include "case.h"
 #include "casefile.h"
 #include "command.h"
 #include "case.h"
 #include "casefile.h"
 #include "command.h"
+#include "dictionary.h"
 #include "lexer.h"
 #include "error.h"
 #include "magic.h"
 #include "lexer.h"
 #include "error.h"
 #include "magic.h"
diff --git a/src/dfm-read.c b/src/dfm-read.c
new file mode 100644 (file)
index 0000000..e12864a
--- /dev/null
@@ -0,0 +1,479 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 1997-2004 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., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#include <config.h>
+#include "dfm-read.h"
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "alloc.h"
+#include "command.h"
+#include "error.h"
+#include "file-handle.h"
+#include "filename.h"
+#include "getline.h"
+#include "lexer.h"
+#include "str.h"
+#include "vfm.h"
+
+#include "debug-print.h"
+
+/* Flags for DFM readers. */
+enum dfm_reader_flags
+  {
+    DFM_EOF = 001,              /* At end-of-file? */
+    DFM_ADVANCE = 002,          /* Read next line on dfm_get_record() call? */
+    DFM_SAW_BEGIN_DATA = 004,   /* For inline_file only, whether we've 
+                                   already read a BEGIN DATA line. */
+    DFM_TABS_EXPANDED = 010,    /* Tabs have been expanded. */
+  };
+
+/* Data file reader. */
+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_*. */
+  };
+
+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(). */
+void
+dfm_close_reader (struct dfm_reader *r)
+{
+  int still_open;
+
+  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. */
+          while ((r->flags & DFM_EOF) == 0)
+            read_record (r);
+          inline_file = NULL;
+        }
+    }
+  if (still_open)
+    return;
+
+  if (r->fh != NULL && r->file.file)
+    {
+      fn_close_ext (&r->file);
+      free (r->file.filename);
+      r->file.filename = NULL;
+    }
+  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
+   "inline file", that is, data included inline in the command
+   file between BEGIN FILE and END FILE.  Returns nonzero only if
+   successful. */
+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;
+    }
+  
+  r = xmalloc (sizeof *r);
+  r->fh = fh;
+  if (fh != NULL) 
+    {
+      r->where.filename = handle_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)
+    {
+      r->file.filename = xstrdup (handle_get_filename (r->fh));
+      r->file.mode = "rb";
+      r->file.file = NULL;
+      r->file.sequence_no = NULL;
+      r->file.param = NULL;
+      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."),
+               handle_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;
+
+  return r;
+}
+
+static int
+read_inline_record (struct dfm_reader *r)
+{
+  if ((r->flags & DFM_SAW_BEGIN_DATA) == 0)
+    {
+      char *s;
+
+      r->flags |= DFM_SAW_BEGIN_DATA;
+
+      /* FIXME: WTF can't this just be done with tokens?
+         Is this really a special case? */
+      do
+        {
+          char *cp;
+
+          if (!getl_read_line ())
+            {
+              msg (SE, _("BEGIN DATA expected."));
+              err_failure ();
+            }
+
+          /* Skip leading whitespace, separate out first
+             word, so that S points to a single word reduced
+             to lowercase. */
+          s = ds_c_str (&getl_buf);
+          while (isspace ((unsigned char) *s))
+            s++;
+          for (cp = s; isalpha ((unsigned char) *cp); cp++)
+            *cp = tolower ((unsigned char) (*cp));
+          ds_truncate (&getl_buf, cp - s);
+        }
+      while (*s == '\0');
+
+      if (!lex_id_match_len ("begin", 5, s, strcspn (s, " \t\r\v\n")))
+        {
+          msg (SE, _("BEGIN DATA expected."));
+          lex_preprocess_line ();
+          return 0;
+        }
+      getl_prompt = GETL_PRPT_DATA;
+    }
+      
+  if (!getl_read_line ())
+    {
+      msg (SE, _("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."));
+      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;
+    }
+
+  ds_replace (&r->line, ds_c_str (&getl_buf));
+  return 1;
+}
+
+static int
+read_file_record (struct dfm_reader *r)
+{
+  assert (r->fh != NULL);
+  if (handle_get_mode (r->fh) == MODE_TEXT)
+    {
+      ds_clear (&r->line);
+      if (!ds_gets (&r->line, r->file.file)) 
+        {
+          if (ferror (r->file.file))
+            {
+              msg (ME, _("Error reading file %s: %s."),
+                   handle_get_name (r->fh), strerror (errno));
+              err_cond_fail ();
+            }
+          return 0;
+        }
+    }
+  else if (handle_get_mode (r->fh) == MODE_BINARY)
+    {
+      size_t record_width = handle_get_record_width (r->fh);
+      size_t amt;
+
+      if (ds_length (&r->line) < record_width) 
+        ds_rpad (&r->line, record_width, 0);
+          
+      amt = fread (ds_c_str (&r->line), 1, record_width,
+                   r->file.file);
+      if (record_width != amt)
+        {
+          if (ferror (r->file.file))
+            msg (ME, _("Error reading file %s: %s."),
+                 handle_get_name (r->fh), strerror (errno));
+          else if (amt != 0)
+            msg (ME, _("%s: Partial record at end of file."),
+                 handle_get_name (r->fh));
+          else
+            return 0;
+
+          err_cond_fail ();
+          return 0;
+        }
+    }
+  else
+    assert (0);
+
+  r->where.line_number++;
+
+  return 1;
+}
+
+/* Reads a record from R, setting the current position to the
+   start of the line.  If an error occurs or end-of-file is
+   encountered, the current line is set to null. */
+static void
+read_record (struct dfm_reader *r)
+{
+  int success = r->fh != NULL ? read_file_record (r) : read_inline_record (r);
+  if (success)
+    r->pos = 0;
+  else
+    r->flags |= DFM_EOF;
+}
+
+/* Returns nonzero if end of file has been reached on HANDLE.
+   Reads forward in HANDLE's file, if necessary to tell. */
+int
+dfm_eof (struct dfm_reader *r) 
+{
+  if (r->flags & DFM_ADVANCE)
+    {
+      r->flags &= ~DFM_ADVANCE;
+      if ((r->flags & DFM_EOF) == 0)
+        read_record (r);
+      else
+        {
+          if (r->fh != NULL)
+            msg (SE, _("Attempt to read beyond end-of-file on file %s."),
+                 handle_get_name (r->fh));
+          else
+            msg (SE, _("Attempt to read beyond END DATA."));
+          err_cond_fail ();
+        }
+    }
+
+  return (r->flags & DFM_EOF) != 0;
+}
+
+/* Returns the current record in the file corresponding to
+   HANDLE.  Aborts if reading from the file is necessary or at
+   end of file, so call dfm_eof() first.  Sets *LINE to the line,
+   which is not null-terminated.  The caller must not free or
+   modify the returned string.  */
+void
+dfm_get_record (struct dfm_reader *r, struct len_string *line)
+{
+  assert ((r->flags & DFM_ADVANCE) == 0);
+  assert ((r->flags & DFM_EOF) == 0);
+  assert (r->pos <= ds_length (&r->line));
+
+  line->string = ds_data (&r->line) + r->pos;
+  line->length = ds_length (&r->line) - r->pos;
+}
+
+/* Expands tabs in the current line into the equivalent number of
+   spaces, if appropriate for this kind of file.  Aborts if
+   reading from the file is necessary or at end of file, so call
+   dfm_eof() first.*/
+void
+dfm_expand_tabs (struct dfm_reader *r) 
+{
+  struct string temp;
+  size_t ofs, new_pos, tab_width;
+
+  assert ((r->flags & DFM_ADVANCE) == 0);
+  assert ((r->flags & DFM_EOF) == 0);
+  assert (r->pos <= ds_length (&r->line));
+
+  if (r->flags & DFM_TABS_EXPANDED)
+    return;
+  r->flags |= DFM_TABS_EXPANDED;
+
+  if (r->fh != NULL
+      && (handle_get_mode (r->fh) == MODE_BINARY
+          || handle_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 ? handle_get_tab_width (r->fh) : 8;
+  ds_clear (&r->scratch);
+  new_pos = 0;
+  for (ofs = 0; ofs < ds_length (&r->line); ofs++)
+    {
+      unsigned char c;
+      
+      if (ofs == r->pos)
+        new_pos = ds_length (&r->scratch);
+
+      c = ds_c_str (&r->line)[ofs];
+      if (c != '\t')
+        ds_putc (&r->scratch, c);
+      else 
+        {
+          do
+            ds_putc (&r->scratch, ' ');
+          while (ds_length (&r->scratch) % tab_width != 0);
+        }
+    }
+
+  /* Swap r->line and r->scratch and set new r->pos. */
+  temp = r->line;
+  r->line = r->scratch;
+  r->scratch = temp;
+  r->pos = new_pos;
+}
+
+/* Causes dfm_get_record() to read in the next record the next time it
+   is executed on file HANDLE. */
+void
+dfm_forward_record (struct dfm_reader *r)
+{
+  r->flags |= DFM_ADVANCE;
+}
+
+/* Cancels the effect of any previous dfm_fwd_record() executed
+   on file HANDLE.  Sets the current line to begin in the 1-based
+   column COLUMN.  */
+void
+dfm_reread_record (struct dfm_reader *r, size_t column)
+{
+  r->flags &= ~DFM_ADVANCE;
+  if (column < 1)
+    r->pos = 0;
+  else if (column > ds_length (&r->line))
+    r->pos = ds_length (&r->line);
+  else
+    r->pos = column - 1;
+}
+
+/* Sets the current line to begin COLUMNS characters following
+   the current start. */
+void
+dfm_forward_columns (struct dfm_reader *r, size_t columns)
+{
+  dfm_reread_record (r, (r->pos + 1) + columns);
+}
+
+/* Returns the 1-based column to which the line pointer in HANDLE
+   is set.  Unless dfm_reread_record() or dfm_forward_columns()
+   have been called, this is 1. */
+size_t
+dfm_column_start (struct dfm_reader *r)
+{
+  return r->pos + 1;
+}
+
+/* Pushes the filename and line number on the fn/ln stack. */
+void
+dfm_push (struct dfm_reader *r)
+{
+  if (r->fh != NULL)
+    err_push_file_locator (&r->where);
+}
+
+/* Pops the filename and line number from the fn/ln stack. */
+void
+dfm_pop (struct dfm_reader *r)
+{
+  if (r->fh != NULL)
+    err_pop_file_locator (&r->where);
+}
+\f
+/* BEGIN DATA...END DATA procedure. */
+
+/* Perform BEGIN DATA...END DATA as a procedure in itself. */
+int
+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))
+    {
+      msg (SE, _("This command is not valid here since the current "
+                 "input program does not access the inline file."));
+      err_cond_fail ();
+      return CMD_FAILURE;
+    }
+
+  /* Open inline file. */
+  r = dfm_open_reader (NULL);
+  r->flags |= DFM_SAW_BEGIN_DATA;
+
+  /* Input procedure reads from inline file. */
+  getl_prompt = GETL_PRPT_DATA;
+  procedure (NULL, NULL);
+
+  dfm_close_reader (r);
+
+  return CMD_SUCCESS;
+}
diff --git a/src/dfm-read.h b/src/dfm-read.h
new file mode 100644 (file)
index 0000000..f883375
--- /dev/null
@@ -0,0 +1,51 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 1997-9, 2000 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., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#ifndef DFM_READ_H
+#define DFM_READ_H
+
+/* Data file manager (dfm).
+
+   This module is in charge of reading and writing data files (other
+   than system files).  dfm is an fhuser, so see file-handle.h for the
+   fhuser interface. */
+
+#include <stddef.h>
+
+struct file_handle;
+struct len_string;
+
+/* Input. */
+struct dfm_reader *dfm_open_reader (struct file_handle *);
+void dfm_close_reader (struct dfm_reader *);
+int dfm_eof (struct dfm_reader *);
+void dfm_get_record (struct dfm_reader *, struct len_string *);
+void dfm_expand_tabs (struct dfm_reader *);
+
+/* Line control. */
+void dfm_forward_record (struct dfm_reader *);
+void dfm_reread_record (struct dfm_reader *, size_t column);
+void dfm_forward_columns (struct dfm_reader *, size_t columns);
+size_t dfm_column_start (struct dfm_reader *);
+
+/* File stack. */
+void dfm_push (struct dfm_reader *);
+void dfm_pop (struct dfm_reader *);
+
+#endif /* dfm-read.h */
diff --git a/src/dfm-write.c b/src/dfm-write.c
new file mode 100644 (file)
index 0000000..762a534
--- /dev/null
@@ -0,0 +1,127 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 1997-2004 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., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#include <config.h>
+#include "dfm-write.h"
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include "alloc.h"
+#include "error.h"
+#include "file-handle.h"
+#include "filename.h"
+#include "str.h"
+
+/* Data file writer. */
+struct dfm_writer
+  {
+    struct file_handle *fh;     /* File handle. */
+    struct file_ext file;      /* Associated file. */
+    char *bounce;               /* Bounce buffer for fixed-size fields. */
+  };
+
+/* Opens a file handle for writing as a data file. */
+struct dfm_writer *
+dfm_open_writer (struct file_handle *fh)
+{
+  struct dfm_writer *w;
+  void **aux;
+  
+  aux = fh_open (fh, "data file", "ws");
+  if (aux == NULL)
+    return NULL;
+  if (*aux != NULL)
+    return *aux;
+
+  w = *aux = xmalloc (sizeof *w);
+  w->fh = fh;
+  w->file.file = NULL;
+  w->bounce = NULL;
+
+  w->file.filename = xstrdup (handle_get_filename (w->fh));
+  w->file.mode = "wb";
+  w->file.file = NULL;
+  w->file.sequence_no = NULL;
+  w->file.param = NULL;
+  w->file.postopen = NULL;
+  w->file.preclose = NULL;
+      
+  if (!fn_open_ext (&w->file))
+    {
+      msg (ME, _("An error occurred while opening \"%s\" for writing "
+                 "as a data file: %s."),
+           handle_get_filename (w->fh), strerror (errno));
+      goto error;
+    }
+
+  return w;
+
+ error:
+  err_cond_fail ();
+  dfm_close_writer (w);
+  return NULL;
+}
+
+/* Writes record REC having length LEN to the file corresponding to
+   HANDLE.  REC is not null-terminated.  Returns nonzero on success,
+   zero on failure. */
+int
+dfm_put_record (struct dfm_writer *w, const char *rec, size_t len)
+{
+  assert (w != NULL);
+
+  if (handle_get_mode (w->fh) == MODE_BINARY
+      && len < handle_get_record_width (w->fh))
+    {
+      size_t rec_width = handle_get_record_width (w->fh);
+      if (w->bounce == NULL)
+        w->bounce = xmalloc (rec_width);
+      memcpy (w->bounce, rec, len);
+      memset (&w->bounce[len], 0, rec_width - len);
+      rec = w->bounce;
+      len = rec_width;
+    }
+
+  if (fwrite (rec, len, 1, w->file.file) != 1)
+    {
+      msg (ME, _("Error writing file %s: %s."),
+           handle_get_name (w->fh), strerror (errno));
+      err_cond_fail ();
+      return 0;
+    }
+
+  return 1;
+}
+
+/* Closes data file writer W. */
+void
+dfm_close_writer (struct dfm_writer *w)
+{
+  if (fh_close (w->fh, "data file", "ws"))
+    return;
+  
+  if (w->file.file)
+    {
+      fn_close_ext (&w->file);
+      free (w->file.filename);
+      w->file.filename = NULL;
+    }
+  free (w->bounce);
+  free (w);
+}
diff --git a/src/dfm-write.h b/src/dfm-write.h
new file mode 100644 (file)
index 0000000..88b5768
--- /dev/null
@@ -0,0 +1,32 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 1997-9, 2000 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., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#ifndef DFM_WRITE_H
+#define DFM_WRITE_H
+
+/* Writing data files. */
+
+#include <stddef.h>
+
+struct file_handle;
+struct dfm_writer *dfm_open_writer (struct file_handle *);
+void dfm_close_writer (struct dfm_writer *);
+int dfm_put_record (struct dfm_writer *, const char *rec, size_t len);
+
+#endif /* dfm-write.h */
diff --git a/src/dfm.c b/src/dfm.c
deleted file mode 100644 (file)
index 8216079..0000000
--- a/src/dfm.c
+++ /dev/null
@@ -1,625 +0,0 @@
-/* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 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., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
-
-#include <config.h>
-#include "error.h"
-#include "dfm.h"
-#include <ctype.h>
-#include <errno.h>
-#include <stdlib.h>
-#include "alloc.h"
-#include "command.h"
-#include "error.h"
-#include "file-handle.h"
-#include "filename.h"
-#include "getline.h"
-#include "lexer.h"
-#include "misc.h"
-#include "str.h"
-#include "vfm.h"
-
-#include "debug-print.h"
-
-/* Flags for DFM readers. */
-enum dfm_reader_flags
-  {
-    DFM_EOF = 001,              /* At end-of-file? */
-    DFM_ADVANCE = 002,          /* Read next line on dfm_get_record() call? */
-    DFM_SAW_BEGIN_DATA = 004,   /* For inline_file only, whether we've 
-                                   already read a BEGIN DATA line. */
-    DFM_TABS_EXPANDED = 010,    /* Tabs have been expanded. */
-  };
-
-/* file_handle extension structure. */
-struct dfm_reader_ext
-  {
-    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_*. */
-  };
-
-static struct fh_ext_class dfm_r_class;
-
-static void read_record (struct file_handle *h);
-
-/* Asserts that H represents a DFM reader and returns H->ext
-   converted to a struct dfm_reader_ext *. */
-static inline struct dfm_reader_ext *
-get_reader (struct file_handle *h) 
-{
-  assert (h != NULL);
-  assert (h->class == &dfm_r_class);
-  assert (h->ext != NULL);
-
-  return h->ext;
-}
-
-/* Closes file handle H opened by dfm_open_for_reading(). */
-static void
-close_reader (struct file_handle *h)
-{
-  struct dfm_reader_ext *ext = get_reader (h);
-
-  /* Skip any remaining data on the inline file. */
-  if (h == inline_file)
-    while ((ext->flags & DFM_EOF) == 0)
-      read_record (h);
-      
-  msg (VM (2), _("%s: Closing data-file handle %s."),
-       handle_get_filename (h), handle_get_name (h));
-  assert (h->class == &dfm_r_class);
-  if (ext->file.file)
-    {
-      fn_close_ext (&ext->file);
-      free (ext->file.filename);
-      ext->file.filename = NULL;
-    }
-  ds_destroy (&ext->line);
-  ds_destroy (&ext->scratch);
-  free (ext);
-}
-
-/* Opens a file handle for reading as a data file.  Returns
-   nonzero only if successful. */
-int
-dfm_open_for_reading (struct file_handle *h)
-{
-  struct dfm_reader_ext *ext;
-
-  if (h->class != NULL)
-    {
-      if (h->class == &dfm_r_class)
-        return 1;
-      else
-        {
-          msg (ME, _("Cannot read from file %s already opened for %s."),
-               handle_get_name (h), gettext (h->class->name));
-          return 0;
-        }
-    }
-
-  ext = xmalloc (sizeof *ext);
-  ext->where.filename = handle_get_filename (h);
-  ext->where.line_number = 0;
-  ext->file.file = NULL;
-  ds_init (&ext->line, 64);
-  ds_init (&ext->scratch, 0);
-  ext->flags = DFM_ADVANCE;
-
-  msg (VM (1), _("%s: Opening data-file handle %s for reading."),
-       handle_get_filename (h), handle_get_name (h));
-  
-  assert (h != NULL);
-  if (h != inline_file)
-    {
-      ext->file.filename = xstrdup (handle_get_filename (h));
-      ext->file.mode = "rb";
-      ext->file.file = NULL;
-      ext->file.sequence_no = NULL;
-      ext->file.param = NULL;
-      ext->file.postopen = NULL;
-      ext->file.preclose = NULL;
-      if (!fn_open_ext (&ext->file))
-       {
-         msg (ME, _("Could not open \"%s\" for reading "
-                     "as a data file: %s."),
-               handle_get_filename (h), strerror (errno));
-          goto error;
-       }
-    }
-
-  h->class = &dfm_r_class;
-  h->ext = ext;
-  return 1;
-
- error:
-  err_cond_fail ();
-  free (ext);
-  return 0;
-}
-
-/* Reads a record from H->EXT->FILE into H->EXT->LINE, setting
-   H->EXT->PTR to H->EXT->LINE, and setting H->EXT-LEN to the length
-   of the line.  The line is not null-terminated.  If an error occurs
-   or end-of-file is encountered, H->EXT->LINE is set to NULL. */
-static void
-read_record (struct file_handle *h)
-{
-  struct dfm_reader_ext *ext = get_reader (h);
-
-  if (h == inline_file)
-    {
-      if ((ext->flags & DFM_SAW_BEGIN_DATA) == 0)
-        {
-          char *s;
-
-          ext->flags |= DFM_SAW_BEGIN_DATA;
-
-          /* FIXME: WTF can't this just be done with tokens?
-             Is this really a special case? */
-          do
-            {
-              char *cp;
-
-              if (!getl_read_line ())
-                {
-                  msg (SE, _("BEGIN DATA expected."));
-                  err_failure ();
-                }
-
-              /* Skip leading whitespace, separate out first
-                 word, so that S points to a single word reduced
-                 to lowercase. */
-              s = ds_c_str (&getl_buf);
-              while (isspace ((unsigned char) *s))
-                s++;
-              for (cp = s; isalpha ((unsigned char) *cp); cp++)
-                *cp = tolower ((unsigned char) (*cp));
-              ds_truncate (&getl_buf, cp - s);
-            }
-          while (*s == '\0');
-
-          if (!lex_id_match_len ("begin", 5, s, strcspn (s, " \t\r\v\n")))
-            {
-              msg (SE, _("BEGIN DATA expected."));
-              lex_preprocess_line ();
-              goto eof;
-            }
-          getl_prompt = GETL_PRPT_DATA;
-        }
-      
-      if (!getl_read_line ())
-       {
-         msg (SE, _("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."));
-         err_failure ();
-       }
-
-      ext->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));
-         goto eof;
-       }
-
-      ds_replace (&ext->line, ds_c_str (&getl_buf));
-    }
-  else
-    {
-      if (handle_get_mode (h) == MODE_TEXT)
-       {
-          ds_clear (&ext->line);
-          if (!ds_gets (&ext->line, ext->file.file)) 
-            {
-             if (ferror (ext->file.file))
-               {
-                 msg (ME, _("Error reading file %s: %s."),
-                      handle_get_name (h), strerror (errno));
-                 err_cond_fail ();
-               }
-             goto eof;
-           }
-       }
-      else if (handle_get_mode (h) == MODE_BINARY)
-       {
-          size_t record_width = handle_get_record_width (h);
-         size_t amt;
-
-          if (ds_length (&ext->line) < record_width) 
-            ds_rpad (&ext->line, record_width, 0);
-          
-         amt = fread (ds_c_str (&ext->line), 1, record_width,
-                       ext->file.file);
-         if (record_width != amt)
-           {
-             if (ferror (ext->file.file))
-               msg (ME, _("Error reading file %s: %s."),
-                    handle_get_name (h), strerror (errno));
-             else if (amt != 0)
-               msg (ME, _("%s: Partial record at end of file."),
-                    handle_get_name (h));
-             else
-               goto eof;
-
-             err_cond_fail ();
-             goto eof;
-           }
-       }
-      else
-       assert (0);
-
-      ext->where.line_number++;
-    }
-
-  ext->pos = 0;
-  return;
-
-eof:
-  /* Hit eof or an error, clean up everything. */
-  ext->flags |= DFM_EOF;
-}
-
-/* Returns nonzero if end of file has been reached on HANDLE.
-   Reads forward in HANDLE's file, if necessary to tell. */
-int
-dfm_eof (struct file_handle *h) 
-{
-  struct dfm_reader_ext *ext = get_reader (h);
-  if (ext->flags & DFM_ADVANCE)
-    {
-      ext->flags &= ~DFM_ADVANCE;
-      if ((ext->flags & DFM_EOF) == 0)
-        read_record (h);
-      else
-        {
-          msg (SE, _("Attempt to read beyond end-of-file on file %s."),
-               handle_get_name (h));
-          err_cond_fail ();
-        }
-    }
-
-  return (ext->flags & DFM_EOF) != 0;
-}
-
-/* Returns the current record in the file corresponding to
-   HANDLE.  Aborts if reading from the file is necessary or at
-   end of file, so call dfm_eof() first.  Sets *LINE to the line,
-   which is not null-terminated.  The caller must not free or
-   modify the returned string.  */
-void
-dfm_get_record (struct file_handle *h, struct len_string *line)
-{
-  struct dfm_reader_ext *ext = get_reader (h);
-  assert ((ext->flags & DFM_ADVANCE) == 0);
-  assert ((ext->flags & DFM_EOF) == 0);
-  assert (ext->pos <= ds_length (&ext->line));
-
-  line->string = ds_data (&ext->line) + ext->pos;
-  line->length = ds_length (&ext->line) - ext->pos;
-}
-
-/* Expands tabs in the current line into the equivalent number of
-   spaces, if appropriate for this kind of file.  Aborts if
-   reading from the file is necessary or at end of file, so call
-   dfm_eof() first.*/
-void
-dfm_expand_tabs (struct file_handle *h) 
-{
-  struct dfm_reader_ext *ext = get_reader (h);
-  struct string temp;
-  size_t ofs, new_pos, tab_width;
-
-  assert ((ext->flags & DFM_ADVANCE) == 0);
-  assert ((ext->flags & DFM_EOF) == 0);
-  assert (ext->pos <= ds_length (&ext->line));
-
-  if (ext->flags & DFM_TABS_EXPANDED)
-    return;
-  ext->flags |= DFM_TABS_EXPANDED;
-
-  if (handle_get_mode (h) == MODE_BINARY
-      || handle_get_tab_width (h) == 0
-      || memchr (ds_c_str (&ext->line), '\t', ds_length (&ext->line)) == NULL)
-    return;
-
-  /* Expand tabs from ext->line into ext->scratch, and figure out
-     new value for ext->pos. */
-  tab_width = handle_get_tab_width (h);
-  ds_clear (&ext->scratch);
-  new_pos = 0;
-  for (ofs = 0; ofs < ds_length (&ext->line); ofs++)
-    {
-      unsigned char c;
-      
-      if (ofs == ext->pos)
-        new_pos = ds_length (&ext->scratch);
-
-      c = ds_c_str (&ext->line)[ofs];
-      if (c != '\t')
-        ds_putc (&ext->scratch, c);
-      else 
-        {
-          do
-            ds_putc (&ext->scratch, ' ');
-          while (ds_length (&ext->scratch) % tab_width != 0);
-        }
-    }
-
-  /* Swap ext->line and ext->scratch and set new ext->pos. */
-  temp = ext->line;
-  ext->line = ext->scratch;
-  ext->scratch = temp;
-  ext->pos = new_pos;
-}
-
-/* Causes dfm_get_record() to read in the next record the next time it
-   is executed on file HANDLE. */
-void
-dfm_forward_record (struct file_handle *h)
-{
-  struct dfm_reader_ext *ext = get_reader (h);
-  ext->flags |= DFM_ADVANCE;
-}
-
-/* Cancels the effect of any previous dfm_fwd_record() executed
-   on file HANDLE.  Sets the current line to begin in the 1-based
-   column COLUMN.  */
-void
-dfm_reread_record (struct file_handle *h, size_t column)
-{
-  struct dfm_reader_ext *ext = get_reader (h);
-  ext->flags &= ~DFM_ADVANCE;
-  if (column < 1)
-    ext->pos = 0;
-  else if (column > ds_length (&ext->line))
-    ext->pos = ds_length (&ext->line);
-  else
-    ext->pos = column - 1;
-}
-
-/* Sets the current line to begin COLUMNS characters following
-   the current start. */
-void
-dfm_forward_columns (struct file_handle *h, size_t columns)
-{
-  struct dfm_reader_ext *ext = get_reader (h);
-  dfm_reread_record (h, (ext->pos + 1) + columns);
-}
-
-/* Returns the 1-based column to which the line pointer in HANDLE
-   is set.  Unless dfm_reread_record() or dfm_forward_columns()
-   have been called, this is 1. */
-size_t
-dfm_column_start (struct file_handle *h)
-{
-  struct dfm_reader_ext *ext = get_reader (h);
-  return ext->pos + 1;
-}
-
-/* Pushes the filename and line number on the fn/ln stack. */
-void
-dfm_push (struct file_handle *h)
-{
-  struct dfm_reader_ext *ext = get_reader (h);
-  if (h != inline_file)
-    err_push_file_locator (&ext->where);
-}
-
-/* Pops the filename and line number from the fn/ln stack. */
-void
-dfm_pop (struct file_handle *h)
-{
-  struct dfm_reader_ext *ext = get_reader (h);
-  if (h != inline_file)
-    err_pop_file_locator (&ext->where);
-}
-
-/* DFM reader class. */
-static struct fh_ext_class dfm_r_class =
-{
-  1,
-  N_("reading as a data file"),
-  close_reader,
-};
-\f
-/* file_handle extension structure. */
-struct dfm_writer_ext
-  {
-    struct file_ext file;      /* Associated file. */
-    struct file_locator where;  /* Current location in data file. */
-    char *bounce;               /* Bounce buffer for fixed-size fields. */
-  };
-
-static struct fh_ext_class dfm_w_class;
-
-/* Opens a file handle for writing as a data file. */
-int
-dfm_open_for_writing (struct file_handle *h)
-{
-  struct dfm_writer_ext *ext;
-  
-  if (h->class != NULL)
-    {
-      if (h->class == &dfm_w_class)
-        return 1;
-      else
-        {
-          msg (ME, _("Cannot write to file %s already opened for %s."),
-               handle_get_name (h), gettext (h->class->name));
-          err_cond_fail ();
-          return 0;
-        }
-    }
-
-  ext = xmalloc (sizeof *ext);
-  ext->where.filename = handle_get_filename (h);
-  ext->where.line_number = 0;
-  ext->file.file = NULL;
-  ext->bounce = NULL;
-
-  msg (VM (1), _("%s: Opening data-file handle %s for writing."),
-       handle_get_filename (h), handle_get_name (h));
-  
-  assert (h != NULL);
-  if (h == inline_file)
-    {
-      msg (ME, _("Cannot open the inline file for writing."));
-      goto error;
-    }
-
-  ext->file.filename = xstrdup (handle_get_filename (h));
-  ext->file.mode = "wb";
-  ext->file.file = NULL;
-  ext->file.sequence_no = NULL;
-  ext->file.param = NULL;
-  ext->file.postopen = NULL;
-  ext->file.preclose = NULL;
-      
-  if (!fn_open_ext (&ext->file))
-    {
-      msg (ME, _("An error occurred while opening \"%s\" for writing "
-                 "as a data file: %s."),
-           handle_get_filename (h), strerror (errno));
-      goto error;
-    }
-
-  h->class = &dfm_w_class;
-  h->ext = ext;
-  return 1;
-
- error:
-  free (ext);
-  err_cond_fail ();
-  return 0;
-}
-
-/* Writes record REC having length LEN to the file corresponding to
-   HANDLE.  REC is not null-terminated.  Returns nonzero on success,
-   zero on failure. */
-int
-dfm_put_record (struct file_handle *h, const char *rec, size_t len)
-{
-  struct dfm_writer_ext *ext;
-
-  assert (h != NULL);
-  assert (h->class == &dfm_w_class);
-  assert (h->ext != NULL);
-
-  ext = h->ext;
-  if (handle_get_mode (h) == MODE_BINARY && len < handle_get_record_width (h))
-    {
-      size_t rec_width = handle_get_record_width (h);
-      if (ext->bounce == NULL)
-        ext->bounce = xmalloc (rec_width);
-      memcpy (ext->bounce, rec, len);
-      memset (&ext->bounce[len], 0, rec_width - len);
-      rec = ext->bounce;
-      len = rec_width;
-    }
-
-  if (fwrite (rec, len, 1, ext->file.file) != 1)
-    {
-      msg (ME, _("Error writing file %s: %s."),
-           handle_get_name (h), strerror (errno));
-      err_cond_fail ();
-      return 0;
-    }
-
-  return 1;
-}
-
-/* Closes file handle H opened by dfm_open_for_writing(). */
-static void
-close_writer (struct file_handle *h)
-{
-  struct dfm_writer_ext *ext;
-
-  assert (h->class == &dfm_w_class);
-  ext = h->ext;
-
-  msg (VM (2), _("%s: Closing data-file handle %s."),
-       handle_get_filename (h), handle_get_name (h));
-  if (ext->file.file)
-    {
-      fn_close_ext (&ext->file);
-      free (ext->file.filename);
-      ext->file.filename = NULL;
-    }
-  free (ext->bounce);
-  free (ext);
-}
-
-/* DFM writer class. */
-static struct fh_ext_class dfm_w_class =
-{
-  2,
-  N_("writing as a data file"),
-  close_writer,
-};
-\f
-/* BEGIN DATA...END DATA procedure. */
-
-/* Perform BEGIN DATA...END DATA as a procedure in itself. */
-int
-cmd_begin_data (void)
-{
-  struct dfm_reader_ext *ext;
-
-  /* FIXME: figure out the *exact* conditions, not these really
-     lenient conditions. */
-  if (vfm_source == NULL
-      || case_source_is_class (vfm_source, &storage_source_class))
-    {
-      msg (SE, _("This command is not valid here since the current "
-                 "input program does not access the inline file."));
-      err_cond_fail ();
-      return CMD_FAILURE;
-    }
-
-  /* Initialize inline_file. */
-  msg (VM (1), _("inline file: Opening for reading."));
-  dfm_open_for_reading (inline_file);
-  ext = inline_file->ext;
-  ext->flags |= DFM_SAW_BEGIN_DATA;
-
-  /* We don't actually read from the inline file.  The input procedure
-     is what reads from it. */
-  getl_prompt = GETL_PRPT_DATA;
-  procedure (NULL, NULL);
-  
-  ext = inline_file->ext;
-  if (ext && (ext->flags & DFM_EOF) == 0)
-    {
-      msg (MW, _("Skipping remaining inline data."));
-      while ((ext->flags & DFM_EOF) == 0)
-        read_record (inline_file);
-    }
-  assert (inline_file->ext == NULL);
-
-  return CMD_SUCCESS;
-}
diff --git a/src/dfm.h b/src/dfm.h
deleted file mode 100644 (file)
index df2307c..0000000
--- a/src/dfm.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 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., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
-
-#if !dfm_h
-#define dfm_h 1
-
-/* Data file manager (dfm).
-
-   This module is in charge of reading and writing data files (other
-   than system files).  dfm is an fhuser, so see file-handle.h for the
-   fhuser interface. */
-
-#include <stddef.h>
-
-struct file_handle;
-struct len_string;
-
-/* Input. */
-int dfm_open_for_reading (struct file_handle *);
-int dfm_eof (struct file_handle *);
-void dfm_get_record (struct file_handle *, struct len_string *);
-void dfm_expand_tabs (struct file_handle *);
-
-void dfm_forward_record (struct file_handle *);
-void dfm_reread_record (struct file_handle *, size_t column);
-void dfm_forward_columns (struct file_handle *, size_t columns);
-size_t dfm_column_start (struct file_handle *);
-
-/* Output. */
-int dfm_open_for_writing (struct file_handle *);
-int dfm_put_record (struct file_handle *, const char *rec, size_t len);
-
-/* File stack. */
-void dfm_push (struct file_handle *);
-void dfm_pop (struct file_handle *);
-
-#endif /* dfm_h */
index 20d64bc525b56e5b271d76612809177d6ef5f933..f1c5f462f3a437170d216822c9de865ccf24f25b 100644 (file)
    02111-1307, USA. */
 
 #include <config.h>
    02111-1307, USA. */
 
 #include <config.h>
-#include "error.h"
+#include "dictionary.h"
 #include <stdlib.h>
 #include "algorithm.h"
 #include "alloc.h"
 #include "case.h"
 #include <stdlib.h>
 #include "algorithm.h"
 #include "alloc.h"
 #include "case.h"
+#include "error.h"
 #include "hash.h"
 #include "misc.h"
 #include "str.h"
 #include "hash.h"
 #include "misc.h"
 #include "str.h"
@@ -124,6 +125,7 @@ dict_clear (struct dictionary *d)
   for (i = 0; i < d->var_cnt; i++) 
     {
       struct variable *v = d->var[i];
   for (i = 0; i < d->var_cnt; i++) 
     {
       struct variable *v = d->var[i];
+      var_clear_aux (v);
       val_labs_destroy (v->val_labs);
       free (v->label);
       free (v); 
       val_labs_destroy (v->val_labs);
       free (v->label);
       free (v); 
@@ -146,6 +148,19 @@ dict_clear (struct dictionary *d)
   dict_clear_vectors (d);
 }
 
   dict_clear_vectors (d);
 }
 
+/* Destroys the aux data for every variable in D, by calling
+   var_clear_aux() for each variable. */
+void
+dict_clear_aux (struct dictionary *d) 
+{
+  int i;
+  
+  assert (d != NULL);
+  
+  for (i = 0; i < d->var_cnt; i++)
+    var_clear_aux (d->var[i]);
+}
+
 /* Clears a dictionary and destroys it. */
 void
 dict_destroy (struct dictionary *d)
 /* Clears a dictionary and destroys it. */
 void
 dict_destroy (struct dictionary *d)
@@ -254,6 +269,8 @@ dict_create_var (struct dictionary *d, const char *name, int width)
   v->write = v->print;
   v->val_labs = val_labs_create (v->width);
   v->label = NULL;
   v->write = v->print;
   v->val_labs = val_labs_create (v->width);
   v->label = NULL;
+  v->aux = NULL;
+  v->aux_dtor = NULL;
 
   /* Update dictionary. */
   if (d->var_cnt >= d->var_cap) 
 
   /* Update dictionary. */
   if (d->var_cnt >= d->var_cap) 
@@ -410,7 +427,10 @@ dict_delete_var (struct dictionary *d, struct variable *v)
   assert (dict_contains_var (d, v));
   assert (d->var[v->index] == v);
 
   assert (dict_contains_var (d, v));
   assert (d->var[v->index] == v);
 
-  /* Remove v from splits, weight, filter variables. */
+  /* Delete aux data. */
+  var_clear_aux (v);
+
+  /* Remove V from splits, weight, filter variables. */
   d->split_cnt = remove_equal (d->split, d->split_cnt, sizeof *d->split,
                                &v,
                                compare_variable_dblptrs, NULL);
   d->split_cnt = remove_equal (d->split, d->split_cnt, sizeof *d->split,
                                &v,
                                compare_variable_dblptrs, NULL);
@@ -420,7 +440,7 @@ dict_delete_var (struct dictionary *d, struct variable *v)
     d->filter = NULL;
   dict_clear_vectors (d);
 
     d->filter = NULL;
   dict_clear_vectors (d);
 
-  /* Remove v from var array. */
+  /* Remove V from var array. */
   d->var_cnt--;
   memmove (d->var + v->index, d->var + v->index + 1,
            (d->var_cnt - v->index) * sizeof *d->var);
   d->var_cnt--;
   memmove (d->var + v->index, d->var + v->index + 1,
            (d->var_cnt - v->index) * sizeof *d->var);
diff --git a/src/dictionary.h b/src/dictionary.h
new file mode 100644 (file)
index 0000000..abe1bb7
--- /dev/null
@@ -0,0 +1,101 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2004 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., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#ifndef DICTIONARY_H
+#define DICTIONARY_H
+
+#include <stddef.h>
+
+/* Dictionary. */ 
+
+struct variable;
+struct dictionary *dict_create (void);
+struct dictionary *dict_clone (const struct dictionary *);
+void dict_clear (struct dictionary *);
+void dict_clear_aux (struct dictionary *);
+void dict_destroy (struct dictionary *);
+
+size_t dict_get_var_cnt (const struct dictionary *);
+struct variable *dict_get_var (const struct dictionary *, size_t idx);
+void dict_get_vars (const struct dictionary *,
+                    struct variable ***vars, size_t *cnt,
+                    unsigned exclude_classes);
+
+struct variable *dict_create_var (struct dictionary *, const char *,
+                                  int width);
+struct variable *dict_create_var_assert (struct dictionary *, const char *,
+                                  int width);
+struct variable *dict_clone_var (struct dictionary *, const struct variable *,
+                                 const char *);
+void dict_rename_var (struct dictionary *, struct variable *, const char *);
+
+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 *);
+void dict_delete_var (struct dictionary *, struct variable *);
+void dict_delete_vars (struct dictionary *,
+                       struct variable *const *, size_t count);
+void dict_reorder_vars (struct dictionary *,
+                        struct variable *const *, size_t count);
+int 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 *);
+double dict_get_case_weight (const struct dictionary *, 
+                            const struct ccase *, int *);
+void dict_set_weight (struct dictionary *, struct variable *);
+
+struct variable *dict_get_filter (const struct dictionary *);
+void dict_set_filter (struct dictionary *, struct variable *);
+
+int dict_get_case_limit (const struct dictionary *);
+void dict_set_case_limit (struct dictionary *, int);
+
+int dict_get_next_value_idx (const struct dictionary *);
+size_t dict_get_case_size (const struct dictionary *);
+
+void dict_compact_values (struct dictionary *);
+size_t dict_get_compacted_value_cnt (const struct dictionary *);
+int *dict_get_compacted_idx_to_fv (const struct dictionary *);
+
+struct variable *const *dict_get_split_vars (const struct dictionary *);
+size_t dict_get_split_cnt (const struct dictionary *);
+void dict_set_split_vars (struct dictionary *,
+                          struct variable *const *, size_t cnt);
+
+const char *dict_get_label (const struct dictionary *);
+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);
+const struct vector *dict_get_vector (const struct dictionary *,
+                                      size_t idx);
+size_t dict_get_vector_cnt (const struct dictionary *);
+const struct vector *dict_lookup_vector (const struct dictionary *,
+                                         const char *name);
+void dict_clear_vectors (struct dictionary *);
+
+#endif /* dictionary.h */
index 844542ad482eb18a9d0a826a61dd7123c2565dbf..e9e0ca7ec252aff8468c96d2992fd21035009a1f 100644 (file)
@@ -27,6 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "alloc.h"
 #include "str.h"
 #include "case.h"
 #include "alloc.h"
 #include "str.h"
 #include "case.h"
+#include "dictionary.h"
 #include "command.h"
 #include "lexer.h"
 #include "error.h"
 #include "command.h"
 #include "lexer.h"
 #include "error.h"
@@ -40,6 +41,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "hash.h"
 #include "casefile.h"
 #include "factor_stats.h"
 #include "hash.h"
 #include "casefile.h"
 #include "factor_stats.h"
+/* (headers) */
 #include "chart.h"
 
 /* (specification)
 #include "chart.h"
 
 /* (specification)
@@ -125,7 +127,7 @@ void np_plot(const struct metrics *m, const char *varname);
 
 
 /* Per Split function */
 
 
 /* Per Split function */
-static void run_examine(const struct casefile *cf, void *cmd_);
+static void run_examine(const struct casefile *cf, void *);
 
 static void output_examine(void);
 
 
 static void output_examine(void);
 
@@ -152,7 +154,7 @@ cmd_examine(void)
 
   totals->stats = xmalloc(sizeof ( struct metrics ) * n_dependent_vars);
 
 
   totals->stats = xmalloc(sizeof ( struct metrics ) * n_dependent_vars);
 
-  multipass_procedure_with_splits (run_examine, &cmd);
+  multipass_procedure_with_splits (run_examine, NULL);
 
 
   hsh_destroy(hash_table_factors);
 
 
   hsh_destroy(hash_table_factors);
@@ -942,7 +944,7 @@ static int bad_weight_warn = 1;
 
 
 static void 
 
 
 static void 
-run_examine(const struct casefile *cf, void *cmd_)
+run_examine(const struct casefile *cf, void *aux UNUSED)
 {
   struct hsh_iterator hi;
   struct factor *fctr;
 {
   struct hsh_iterator hi;
   struct factor *fctr;
@@ -951,8 +953,6 @@ run_examine(const struct casefile *cf, void *cmd_)
   struct ccase c;
   int v;
 
   struct ccase c;
   int v;
 
-  const struct cmd_examine *cmd = (struct cmd_examine *) cmd_;
-
   /* Make sure we haven't got rubbish left over from a 
      previous split */
   if ( hash_table_factors ) 
   /* Make sure we haven't got rubbish left over from a 
      previous split */
   if ( hash_table_factors ) 
index 2ddefec79cb46ac85b3418defd24db750044f92a..0432765825c507a66070e6252216a3bfd5dd93fa 100644 (file)
@@ -39,6 +39,7 @@
 #include <stdio.h>
 #include "case.h"
 #include "data-in.h"
 #include <stdio.h>
 #include "case.h"
 #include "data-in.h"
+#include "dictionary.h"
 #include "error.h"
 #include "julcal/julcal.h"
 #include "magic.h"
 #include "error.h"
 #include "julcal/julcal.h"
 #include "magic.h"
index da475783297ab9d2f5c82333d2ac1f15f6f1fa7c..183d5b0f3fb30167f447b9851622bafa5358aad1 100644 (file)
@@ -18,6 +18,7 @@
    02111-1307, USA. */
 
 #include <config.h>
    02111-1307, USA. */
 
 #include <config.h>
+#include "dictionary.h"
 #include "expr.h"
 #include "exprP.h"
 #include "error.h"
 #include "expr.h"
 #include "exprP.h"
 #include "error.h"
index 5f18202a48069eff675c36c0c17d604333ccc5f0..00a79109601465fdf05f69710421aec690dd427e 100644 (file)
 #if !file_handle_h
 #define file_handle_h 1
 
 #if !file_handle_h
 #define file_handle_h 1
 
-/* File handle provider (fhp).
-
-   This module provides file handles in the form of file_handle
-   structures to the dfm and sfm modules, which are known as file
-   handle users (fhusers).  fhp does not know anything about file
-   contents. */
+/* File handles. */
 
 #include <stddef.h>
 
 #include <stddef.h>
-#include "error.h"
-
-struct file_handle;
-
-/* Services that fhusers provide to fhp. */
-struct fh_ext_class
-  {
-    int magic;                 /* Magic identifier for fhuser. */
-    const char *name;          /* String identifier for fhuser. */
-
-    void (*close) (struct file_handle *); /* Closes the file. */
-  };
-
-/* Mostly-opaque structure. */
-struct file_handle
-  {
-    struct private_file_handle *private;
-    const struct fh_ext_class *class;  /* Polymorphism support. */
-    void *ext;                 /* Extension struct for fhuser use. */
-  };
 
 /* File modes. */
 enum file_handle_mode
 
 /* File modes. */
 enum file_handle_mode
@@ -56,20 +31,16 @@ enum file_handle_mode
     MODE_BINARY                 /* Fixed-length records. */
   };
 
     MODE_BINARY                 /* Fixed-length records. */
   };
 
-/* Pointer to the file handle that corresponds to data in the command
-   file entered via BEGIN DATA/END DATA. */
-extern struct file_handle *inline_file;
-
-/* Initialization. */
-void fh_init_files (void);
+/* Parsing handles. */
+struct file_handle *fh_parse (void);
 
 /* Opening and closing handles. */
 
 /* Opening and closing handles. */
-struct file_handle *fh_parse_file_handle (void);
-void fh_close_handle (struct file_handle *handle);
+void **fh_open (struct file_handle *, const char *type, const char *mode);
+int fh_close (struct file_handle *, const char *type, const char *mode);
 
 /* Handle info. */
 
 /* Handle info. */
-const char *handle_get_name (const struct file_handle *handle);
-const char *handle_get_filename (const struct file_handle *handle);
+const char *handle_get_name (const struct file_handle *);
+const char *handle_get_filename (const struct file_handle *);
 enum file_handle_mode handle_get_mode (const struct file_handle *);
 size_t handle_get_record_width (const struct file_handle *);
 size_t handle_get_tab_width (const struct file_handle *);
 enum file_handle_mode handle_get_mode (const struct file_handle *);
 size_t handle_get_record_width (const struct file_handle *);
 size_t handle_get_tab_width (const struct file_handle *);
index 96aa9486cd894e18b2c1f5dfb33a3b478458191f..9c3a81a366a4bf5de31e657ba27e6a8ee6a8ae3b 100644 (file)
 #include "var.h"
 /* (headers) */
 
 #include "var.h"
 /* (headers) */
 
-/* File handle private data. */
-struct private_file_handle 
+/* File handle. */
+struct file_handle 
   {
   {
+    struct file_handle *next;   /* Next in global list. */
     char *name;                 /* File handle identifier. */
     char *filename;            /* Filename as provided by user. */
     struct file_identity *identity; /* For checking file identity. */
     char *name;                 /* File handle identifier. */
     char *filename;            /* Filename as provided by user. */
     struct file_identity *identity; /* For checking file identity. */
@@ -42,17 +43,14 @@ struct private_file_handle
     enum file_handle_mode mode;        /* File mode. */
     size_t length;             /* Length of fixed-format records. */
     size_t tab_width;           /* Tab width, 0=do not expand tabs. */
     enum file_handle_mode mode;        /* File mode. */
     size_t length;             /* Length of fixed-format records. */
     size_t tab_width;           /* Tab width, 0=do not expand tabs. */
-  };
 
 
-/* Linked list of file handles. */
-struct file_handle_list 
-  {
-    struct file_handle *handle;
-    struct file_handle_list *next;
+    int open_cnt;               /* 0=not open, otherwise # of openers. */
+    const char *type;           /* If open, type of file. */
+    const char *open_mode;      /* "[rw][se]". */
+    void *aux;                  /* Aux data pointer for owner if any. */
   };
 
   };
 
-static struct file_handle_list *file_handles;
-struct file_handle *inline_file;
+static struct file_handle *file_handles;
 
 static struct file_handle *create_file_handle (const char *handle_name,
                                                const char *filename);
 
 static struct file_handle *create_file_handle (const char *handle_name,
                                                const char *filename);
@@ -70,11 +68,11 @@ static struct file_handle *create_file_handle (const char *handle_name,
 static struct file_handle *
 get_handle_with_name (const char *handle_name) 
 {
 static struct file_handle *
 get_handle_with_name (const char *handle_name) 
 {
-  struct file_handle_list *iter;
+  struct file_handle *iter;
 
   for (iter = file_handles; iter != NULL; iter = iter->next)
 
   for (iter = file_handles; iter != NULL; iter = iter->next)
-    if (!strcmp (handle_name, iter->handle->private->name))
-      return iter->handle;
+    if (!strcmp (handle_name, iter->name))
+      return iter;
   return NULL;
 }
 
   return NULL;
 }
 
@@ -82,27 +80,26 @@ static struct file_handle *
 get_handle_for_filename (const char *filename)
 {
   struct file_identity *identity;
 get_handle_for_filename (const char *filename)
 {
   struct file_identity *identity;
-  struct file_handle_list *iter;
+  struct file_handle *iter;
       
   /* First check for a file with the same identity. */
   identity = fn_get_identity (filename);
   if (identity != NULL) 
     {
       for (iter = file_handles; iter != NULL; iter = iter->next)
       
   /* First check for a file with the same identity. */
   identity = fn_get_identity (filename);
   if (identity != NULL) 
     {
       for (iter = file_handles; iter != NULL; iter = iter->next)
-        if (iter->handle->private->identity != NULL
-            && !fn_compare_file_identities (identity,
-                                         iter->handle->private->identity)) 
+        if (iter->identity != NULL
+            && !fn_compare_file_identities (identity, iter->identity))
           {
             fn_free_identity (identity);
           {
             fn_free_identity (identity);
-            return iter->handle
+            return iter; 
           }
       fn_free_identity (identity);
     }
 
   /* Then check for a file with the same name. */
   for (iter = file_handles; iter != NULL; iter = iter->next)
           }
       fn_free_identity (identity);
     }
 
   /* Then check for a file with the same name. */
   for (iter = file_handles; iter != NULL; iter = iter->next)
-    if (!strcmp (filename, iter->handle->private->filename))
-      return iter->handle
+    if (!strcmp (filename, iter->filename))
+      return iter; 
 
   return NULL;
 }
 
   return NULL;
 }
@@ -125,7 +122,7 @@ cmd_file_handle (void)
       msg (SE, _("File handle %s already refers to "
                 "file %s.  File handle cannot be redefined within a "
                  "session."),
       msg (SE, _("File handle %s already refers to "
                 "file %s.  File handle cannot be redefined within a "
                  "session."),
-          tokid, handle->private->filename);
+          tokid, handle->filename);
       return CMD_FAILURE;
     }
 
       return CMD_FAILURE;
     }
 
@@ -153,29 +150,29 @@ cmd_file_handle (void)
   switch (cmd.mode)
     {
     case FH_CHARACTER:
   switch (cmd.mode)
     {
     case FH_CHARACTER:
-      handle->private->mode = MODE_TEXT;
+      handle->mode = MODE_TEXT;
       if (cmd.sbc_tabwidth)
       if (cmd.sbc_tabwidth)
-        handle->private->tab_width = cmd.n_tabwidth;
+        handle->tab_width = cmd.n_tabwidth[0];
       else
       else
-        handle->private->tab_width = 4;
+        handle->tab_width = 4;
       break;
     case FH_IMAGE:
       break;
     case FH_IMAGE:
-      handle->private->mode = MODE_BINARY;
-      if (cmd.n_lrecl == NOT_LONG)
+      handle->mode = 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.  "
                      "Assuming 1024-character records."));
        {
          msg (SE, _("Fixed-length records were specified on /RECFORM, but "
                      "record length was not specified on /LRECL.  "
                      "Assuming 1024-character records."));
-          handle->private->length = 1024;
+          handle->length = 1024;
        }
        }
-      else if (cmd.n_lrecl < 1)
+      else if (cmd.n_lrecl[0] < 1)
        {
          msg (SE, _("Record length (%ld) must be at least one byte.  "
                     "1-character records will be assumed."), cmd.n_lrecl);
        {
          msg (SE, _("Record length (%ld) must be at least one byte.  "
                     "1-character records will be assumed."), cmd.n_lrecl);
-          handle->private->length = 1;
+          handle->length = 1;
        }
       else
        }
       else
-        handle->private->length = cmd.n_lrecl;
+        handle->length = cmd.n_lrecl[0];
       break;
     default:
       assert (0);
       break;
     default:
       assert (0);
@@ -197,63 +194,121 @@ static struct file_handle *
 create_file_handle (const char *handle_name, const char *filename)
 {
   struct file_handle *handle;
 create_file_handle (const char *handle_name, const char *filename)
 {
   struct file_handle *handle;
-  struct file_handle_list *list;
 
   /* Create and initialize file handle. */
   handle = xmalloc (sizeof *handle);
 
   /* Create and initialize file handle. */
   handle = xmalloc (sizeof *handle);
-  handle->private = xmalloc (sizeof *handle->private);
-  handle->private->name = xstrdup (handle_name);
-  handle->private->filename = xstrdup (filename);
-  handle->private->identity = fn_get_identity (filename);
-  handle->private->where.filename = handle->private->filename;
-  handle->private->where.line_number = 0;
-  handle->private->mode = MODE_TEXT;
-  handle->private->length = 1024;
-  handle->private->tab_width = 4;
-  handle->ext = NULL;
-  handle->class = NULL;
-
-  /* Add file handle to global list. */
-  list = xmalloc (sizeof *list);
-  list->handle = handle;
-  list->next = file_handles;
-  file_handles = list;
+  handle->next = file_handles;
+  handle->name = xstrdup (handle_name);
+  handle->filename = xstrdup (filename);
+  handle->identity = fn_get_identity (filename);
+  handle->where.filename = handle->filename;
+  handle->where.line_number = 0;
+  handle->mode = MODE_TEXT;
+  handle->length = 1024;
+  handle->tab_width = 4;
+  handle->open_cnt = 0;
+  handle->type = NULL;
+  handle->open_mode = NULL;
+  handle->aux = NULL;
+  file_handles = handle;
 
   return handle;
 }
 
 
   return handle;
 }
 
-/* Closes the stdio FILE associated with handle H.  Frees internal
-   buffers associated with that file.  Does *not* destroy the file
-   handle H.  (File handles are permanent during a session.)  */
-void
-fh_close_handle (struct file_handle *h)
+static const char *
+mode_name (const char *mode) 
+{
+  assert (mode != NULL);
+  assert (mode[0] == 'r' || mode[0] == 'w');
+
+  return mode[0] == 'r' ? "reading" : "writing";
+}
+
+
+/* Tries to open FILE with the given TYPE and MODE.
+
+   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.
+
+   MODE combines the read or write mode with the sharing mode.
+   The first character is 'r' for read, 'w' for write.  The
+   second character is 's' to permit sharing, 'e' to require
+   exclusive access.
+
+   Returns the address of a void * that the caller can use for
+   data specific to the file handle if successful, or a null
+   pointer on failure.  For exclusive access modes the void *
+   will always be a null pointer at return.  In shared access
+   modes the void * will necessarily be null only if no other
+   sharers are active.
+
+   If successful, references to type and mode are retained, so
+   they should probably be string literals. */
+void **
+fh_open (struct file_handle *h, const char *type, const char *mode) 
 {
 {
-  if (h == NULL)
-    return;
+  assert (h != NULL);
+  assert (type != NULL);
+  assert (mode != NULL);
+  assert (mode[0] == 'r' || mode[0] == 'w');
+  assert (mode[1] == 's' || mode[1] == 'e');
+  assert (mode[2] == '\0');
+
+  if (h->open_cnt != 0) 
+    {
+      if (strcmp (h->type, type))
+        msg (SE, _("Can't open %s as a %s because it is "
+                   "already open as a %s"),
+             handle_get_name (h), type, h->type);
+      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"),
+             handle_get_name (h), type,
+             mode_name (mode), mode_name (h->open_mode));
+      else if (h->open_mode[1] == 'e')
+        msg (SE, _("Can't re-open %s as a %s for %s"),
+             handle_get_name (h), type, mode_name (mode));
+    }
+  else 
+    {
+      h->type = type;
+      h->open_mode = mode;
+      assert (h->aux == NULL);
+    }
+  h->open_cnt++;
 
 
-  if (h->class != NULL)
-    h->class->close (h);
-  h->class = NULL;
-  h->ext = NULL;
+  return &h->aux;
 }
 
 }
 
-/* Initialize the hash of file handles; inserts the "inline file"
-   inline_file. */
-void
-fh_init_files (void)
+/* 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. */
+int
+fh_close (struct file_handle *h, const char *type, const char *mode)
 {
 {
-  if (inline_file == NULL) 
+  assert (h != NULL);
+  assert (h->open_cnt > 0);
+  assert (type != NULL);
+  assert (!strcmp (type, h->type));
+  assert (mode != NULL);
+  assert (!strcmp (mode, h->open_mode));
+
+  h->open_cnt--;
+  if (h->open_cnt == 0) 
     {
     {
-      inline_file = create_file_handle ("INLINE", _("<Inline File>"));
-      inline_file->private->length = 80; 
+      h->type = NULL;
+      h->open_mode = NULL;
+      h->aux = NULL;
     }
     }
+  return h->open_cnt;
 }
 
 /* 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 *
 }
 
 /* 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_file_handle (void)
+fh_parse (void)
 {
   struct file_handle *handle;
 
 {
   struct file_handle *handle;
 
@@ -292,14 +347,16 @@ fh_parse_file_handle (void)
 const char *
 handle_get_name (const struct file_handle *handle)
 {
 const char *
 handle_get_name (const struct file_handle *handle)
 {
-  return handle->private->name;
+  assert (handle != NULL);
+  return handle->name;
 }
 
 /* Returns the name of the file associated with HANDLE. */
 const char *
 handle_get_filename (const struct file_handle *handle) 
 {
 }
 
 /* Returns the name of the file associated with HANDLE. */
 const char *
 handle_get_filename (const struct file_handle *handle) 
 {
-  return handle->private->filename;
+  assert (handle != NULL);
+  return handle->filename;
 }
 
 /* Returns the mode of HANDLE. */
 }
 
 /* Returns the mode of HANDLE. */
@@ -307,7 +364,7 @@ enum file_handle_mode
 handle_get_mode (const struct file_handle *handle) 
 {
   assert (handle != NULL);
 handle_get_mode (const struct file_handle *handle) 
 {
   assert (handle != NULL);
-  return handle->private->mode;
+  return handle->mode;
 }
 
 /* Returns the width of a logical record on HANDLE.  Applicable
 }
 
 /* Returns the width of a logical record on HANDLE.  Applicable
@@ -316,7 +373,7 @@ size_t
 handle_get_record_width (const struct file_handle *handle)
 {
   assert (handle != NULL);
 handle_get_record_width (const struct file_handle *handle)
 {
   assert (handle != NULL);
-  return handle->private->length;
+  return handle->length;
 }
 
 /* Returns the number of characters per tab stop for HANDLE, or
 }
 
 /* Returns the number of characters per tab stop for HANDLE, or
@@ -326,7 +383,7 @@ size_t
 handle_get_tab_width (const struct file_handle *handle) 
 {
   assert (handle != NULL);
 handle_get_tab_width (const struct file_handle *handle) 
 {
   assert (handle != NULL);
-  return handle->private->tab_width;
+  return handle->tab_width;
 }
 
 /*
 }
 
 /*
index ec076146c7725a4b9e1880671cba4a130df0b1f2..fee9ee5abd473f20348d184a9f49a9d161b38ef4 100644 (file)
    02111-1307, USA. */
 
 #include <config.h>
    02111-1307, USA. */
 
 #include <config.h>
-#include "error.h"
 #include <stdlib.h>
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
 #include "data-in.h"
 #include <stdlib.h>
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
 #include "data-in.h"
-#include "dfm.h"
+#include "dfm-read.h"
+#include "dictionary.h"
+#include "error.h"
 #include "file-handle.h"
 #include "format.h"
 #include "lexer.h"
 #include "file-handle.h"
 #include "format.h"
 #include "lexer.h"
@@ -75,7 +76,7 @@ struct record_type
 struct file_type_pgm
   {
     int type;                  /* One of the FTY_* constants. */
 struct file_type_pgm
   {
     int type;                  /* One of the FTY_* constants. */
-    struct file_handle *handle;        /* File handle of input file. */
+    struct dfm_reader *reader;  /* Data file to read. */
     struct col_spec record;    /* RECORD subcommand. */
     struct col_spec case_sbc;  /* CASE subcommand. */
     int wild;                  /* 0=NOWARN, 1=WARN. */
     struct col_spec record;    /* RECORD subcommand. */
     struct col_spec case_sbc;  /* CASE subcommand. */
     int wild;                  /* 0=NOWARN, 1=WARN. */
@@ -97,13 +98,14 @@ static void create_col_var (struct col_spec *c);
 int
 cmd_file_type (void)
 {
 int
 cmd_file_type (void)
 {
-  static struct file_type_pgm *fty;
+  static struct file_type_pgm *fty;     /* FIXME: static? WTF? */
+  struct file_handle *fh = NULL;
 
   /* Initialize. */
   discard_variables ();
 
   fty = xmalloc (sizeof *fty);
 
   /* Initialize. */
   discard_variables ();
 
   fty = xmalloc (sizeof *fty);
-  fty->handle = inline_file;
+  fty->reader = NULL;
   fty->record.name[0] = 0;
   fty->case_sbc.name[0] = 0;
   fty->wild = fty->duplicate = fty->missing = fty->ordered = 0;
   fty->record.name[0] = 0;
   fty->case_sbc.name[0] = 0;
   fty->wild = fty->duplicate = fty->missing = fty->ordered = 0;
@@ -133,8 +135,8 @@ cmd_file_type (void)
       if (lex_match_id ("FILE"))
        {
          lex_match ('=');
       if (lex_match_id ("FILE"))
        {
          lex_match ('=');
-         fty->handle = fh_parse_file_handle ();
-         if (!fty->handle)
+         fh = fh_parse ();
+         if (fh == NULL)
            goto error;
        }
       else if (lex_match_id ("RECORD"))
            goto error;
        }
       else if (lex_match_id ("RECORD"))
@@ -270,9 +272,10 @@ cmd_file_type (void)
        }
     }
 
        }
     }
 
-  if (!dfm_open_for_reading (fty->handle))
+  fty->reader = dfm_open_reader (fh);
+  if (fty->reader == NULL)
     goto error;
     goto error;
-  default_handle = fty->handle;
+  default_handle = fh;
 
   create_col_var (&fty->record);
   if (fty->case_sbc.name[0])
 
   create_col_var (&fty->record);
   if (fty->case_sbc.name[0])
@@ -626,20 +629,20 @@ file_type_source_read (struct case_source *source,
   struct file_type_pgm *fty = source->aux;
   struct fmt_spec format;
 
   struct file_type_pgm *fty = source->aux;
   struct fmt_spec format;
 
-  dfm_push (fty->handle);
+  dfm_push (fty->reader);
 
   format.type = fty->record.fmt;
   format.w = fty->record.nc;
   format.d = 0;
 
   format.type = fty->record.fmt;
   format.w = fty->record.nc;
   format.d = 0;
-  while (!dfm_eof (fty->handle))
+  while (!dfm_eof (fty->reader))
     {
       struct len_string line;
       struct record_type *iter;
       union value v;
       int i;
 
     {
       struct len_string line;
       struct record_type *iter;
       union value v;
       int i;
 
-      dfm_expand_tabs (fty->handle);
-      dfm_get_record (fty->handle, &line);
+      dfm_expand_tabs (fty->reader);
+      dfm_get_record (fty->reader, &line);
       if (formats[fty->record.fmt].cat & FCAT_STRING)
        {
          struct data_in di;
       if (formats[fty->record.fmt].cat & FCAT_STRING)
        {
          struct data_in di;
@@ -689,13 +692,13 @@ file_type_source_read (struct case_source *source,
          if (fty->wild)
            msg (SW, _("Unknown record type %g."), v.f);
        }
          if (fty->wild)
            msg (SW, _("Unknown record type %g."), v.f);
        }
-      dfm_forward_record (fty->handle);
+      dfm_forward_record (fty->reader);
       continue;
 
     found:
       /* Arrive here if there is a matching record_type, which is in
          iter. */
       continue;
 
     found:
       /* Arrive here if there is a matching record_type, which is in
          iter. */
-      dfm_forward_record (fty->handle);
+      dfm_forward_record (fty->reader);
     }
 
 /*  switch(fty->type)
     }
 
 /*  switch(fty->type)
@@ -706,7 +709,7 @@ file_type_source_read (struct case_source *source,
    default: assert(0);
    } */
 
    default: assert(0);
    } */
 
-  dfm_pop (fty->handle);
+  dfm_pop (fty->reader);
 }
 
 static void
 }
 
 static void
@@ -716,6 +719,7 @@ file_type_source_destroy (struct case_source *source)
   struct record_type *iter, *next;
 
   cancel_transformations ();
   struct record_type *iter, *next;
 
   cancel_transformations ();
+  dfm_close_reader (fty->reader);
   for (iter = fty->recs_head; iter; iter = next)
     {
       next = iter->next;
   for (iter = fty->recs_head; iter; iter = next)
     {
       next = iter->next;
index f9162522d54680e09e61107edbc189018eb42ad6..b4f908dfb7ccdaaf3c591b3321ed8cc6c101c830 100644 (file)
@@ -27,6 +27,7 @@
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "lexer.h"
 #include "misc.h"
 #include "error.h"
 #include "lexer.h"
 #include "misc.h"
index 9a69e9723d18618febdd55f27976adf67a7c6f37..8393819c0a0d2bda5a33eb722ac8267a1e3945b0 100644 (file)
@@ -30,6 +30,7 @@
 #include "alloc.h"
 #include "bitvector.h"
 #include "case.h"
 #include "alloc.h"
 #include "bitvector.h"
 #include "case.h"
+#include "dictionary.h"
 #include "hash.h"
 #include "pool.h"
 #include "command.h"
 #include "hash.h"
 #include "pool.h"
 #include "command.h"
@@ -48,6 +49,7 @@
 #include "vfm.h"
 #include "settings.h"
 #include "chart.h"
 #include "vfm.h"
 #include "settings.h"
 #include "chart.h"
+/* (headers) */
 
 #include "debug-print.h"
 
 
 #include "debug-print.h"
 
 /* (declarations) */
 /* (functions) */
 
 /* (declarations) */
 /* (functions) */
 
+/* Statistics. */
+enum
+  {
+    frq_mean = 0, frq_semean, frq_median, frq_mode, frq_stddev, frq_variance,
+    frq_kurt, frq_sekurt, frq_skew, frq_seskew, frq_range, frq_min, frq_max,
+    frq_sum, frq_n_stats
+  };
+
 /* Description of a statistic. */
 struct frq_info
   {
 /* Description of a statistic. */
 struct frq_info
   {
@@ -129,7 +139,6 @@ struct percentile
 
 static void add_percentile (double x) ;
 
 
 static void add_percentile (double x) ;
 
-
 static struct percentile *percentiles;
 static int n_percentiles;
 
 static struct percentile *percentiles;
 static int n_percentiles;
 
@@ -179,12 +188,31 @@ static struct variable **v_variables;
 static struct pool *int_pool;  /* Integer mode. */
 static struct pool *gen_pool;  /* General mode. */
 
 static struct pool *int_pool;  /* Integer mode. */
 static struct pool *gen_pool;  /* General mode. */
 
-/* Easier access to a_statistics. */
-#define stat cmd.a_statistics
+/* Per-variable frequency data. */
+struct var_freqs
+  {
+    /* Freqency table. */
+    struct freq_tab tab;       /* Frequencies table to use. */
+
+    /* Percentiles. */
+    int n_groups;              /* Number of groups. */
+    double *groups;            /* Groups. */
+
+    /* Statistics. */
+    double stat[frq_n_stats];
+  };
+
+static inline struct var_freqs *
+get_var_freqs (struct variable *v)
+{
+  assert (v != NULL);
+  assert (v->aux != NULL);
+  return v->aux;
+}
 
 static void determine_charts (void);
 
 
 static void determine_charts (void);
 
-static void calc_stats (struct variable * v, double d[frq_n_stats]);
+static void calc_stats (struct variable *v, double d[frq_n_stats]);
 
 static void precalc (void *);
 static int calc (struct ccase *, void *);
 
 static void precalc (void *);
 static int calc (struct ccase *, void *);
@@ -233,9 +261,6 @@ internal_cmd_frequencies (void)
   n_variables = 0;
   v_variables = NULL;
 
   n_variables = 0;
   v_variables = NULL;
 
-  for (i = 0; i < dict_get_var_cnt (default_dict); i++)
-    dict_get_var(default_dict, i)->p.frq.used = 0;
-
   if (!parse_frequencies (&cmd))
     return CMD_FAILURE;
 
   if (!parse_frequencies (&cmd))
     return CMD_FAILURE;
 
@@ -244,14 +269,14 @@ internal_cmd_frequencies (void)
 
   /* Figure out statistics to calculate. */
   stats = 0;
 
   /* Figure out statistics to calculate. */
   stats = 0;
-  if (stat[FRQ_ST_DEFAULT] || !cmd.sbc_statistics)
+  if (cmd.a_statistics[FRQ_ST_DEFAULT] || !cmd.sbc_statistics)
     stats |= frq_default;
     stats |= frq_default;
-  if (stat[FRQ_ST_ALL])
+  if (cmd.a_statistics[FRQ_ST_ALL])
     stats |= frq_all;
   if (cmd.sort != FRQ_AVALUE && cmd.sort != FRQ_DVALUE)
     stats &= ~frq_median;
   for (i = 0; i < frq_n_stats; i++)
     stats |= frq_all;
   if (cmd.sort != FRQ_AVALUE && cmd.sort != FRQ_DVALUE)
     stats &= ~frq_median;
   for (i = 0; i < frq_n_stats; i++)
-    if (stat[st_name[i].st_indx])
+    if (cmd.a_statistics[st_name[i].st_indx])
       stats |= BIT_INDEX (i);
   if (stats & frq_kurt)
     stats |= frq_sekurt;
       stats |= BIT_INDEX (i);
   if (stats & frq_kurt)
     stats |= frq_sekurt;
@@ -416,9 +441,9 @@ calc (struct ccase *c, void *aux UNUSED)
     {
       struct variable *v = v_variables[i];
       const union value *val = case_data (c, v->fv);
     {
       struct variable *v = v_variables[i];
       const union value *val = case_data (c, v->fv);
-      struct freq_tab *ft = &v->p.frq.tab;
+      struct freq_tab *ft = &get_var_freqs (v)->tab;
 
 
-      switch (v->p.frq.tab.mode)
+      switch (ft->mode)
        {
          case FRQM_GENERAL:
            {
        {
          case FRQM_GENERAL:
            {
@@ -439,15 +464,15 @@ calc (struct ccase *c, void *aux UNUSED)
        case FRQM_INTEGER:
          /* Integer mode. */
          if (val->f == SYSMIS)
        case FRQM_INTEGER:
          /* Integer mode. */
          if (val->f == SYSMIS)
-           v->p.frq.tab.sysmis += weight;
+           ft->sysmis += weight;
          else if (val->f > INT_MIN+1 && val->f < INT_MAX-1)
            {
              int i = val->f;
          else if (val->f > INT_MIN+1 && val->f < INT_MAX-1)
            {
              int i = val->f;
-             if (i >= v->p.frq.tab.min && i <= v->p.frq.tab.max)
-               v->p.frq.tab.vector[i - v->p.frq.tab.min] += weight;
+             if (i >= ft->min && i <= ft->max)
+               ft->vector[i - ft->min] += weight;
            }
          else
            }
          else
-           v->p.frq.tab.out_of_range += weight;
+           ft->out_of_range += weight;
          break;
        default:
          assert (0);
          break;
        default:
          assert (0);
@@ -469,8 +494,9 @@ precalc (void *aux UNUSED)
   for (i = 0; i < n_variables; i++)
     {
       struct variable *v = v_variables[i];
   for (i = 0; i < n_variables; i++)
     {
       struct variable *v = v_variables[i];
+      struct freq_tab *ft = &get_var_freqs (v)->tab;
 
 
-      if (v->p.frq.tab.mode == FRQM_GENERAL)
+      if (ft->mode == FRQM_GENERAL)
        {
           hsh_hash_func *hash;
          hsh_compare_func *compare;
        {
           hsh_hash_func *hash;
          hsh_compare_func *compare;
@@ -485,16 +511,16 @@ precalc (void *aux UNUSED)
               hash = hash_value_alpha;
               compare = compare_value_alpha_a;
             }
               hash = hash_value_alpha;
               compare = compare_value_alpha_a;
             }
-         v->p.frq.tab.data = hsh_create (16, compare, hash, NULL, v);
+         ft->data = hsh_create (16, compare, hash, NULL, v);
        }
       else
        {
          int j;
 
        }
       else
        {
          int j;
 
-         for (j = (v->p.frq.tab.max - v->p.frq.tab.min); j >= 0; j--)
-           v->p.frq.tab.vector[j] = 0.0;
-         v->p.frq.tab.out_of_range = 0.0;
-         v->p.frq.tab.sysmis = 0.0;
+         for (j = (ft->max - ft->min); j >= 0; j--)
+           ft->vector[j] = 0.0;
+         ft->out_of_range = 0.0;
+         ft->sysmis = 0.0;
        }
     }
 }
        }
     }
 }
@@ -509,13 +535,15 @@ postcalc (void *aux UNUSED)
   for (i = 0; i < n_variables; i++)
     {
       struct variable *v = v_variables[i];
   for (i = 0; i < n_variables; i++)
     {
       struct variable *v = v_variables[i];
+      struct var_freqs *vf = get_var_freqs (v);
+      struct freq_tab *ft = &vf->tab;
       int n_categories;
       int dumped_freq_tab = 1;
 
       postprocess_freq_tab (v);
 
       /* Frequencies tables. */
       int n_categories;
       int dumped_freq_tab = 1;
 
       postprocess_freq_tab (v);
 
       /* Frequencies tables. */
-      n_categories = v->p.frq.tab.n_valid + v->p.frq.tab.n_missing;
+      n_categories = ft->n_valid + ft->n_missing;
       if (cmd.table == FRQ_TABLE
          || (cmd.table == FRQ_LIMIT && n_categories <= cmd.limit))
        switch (cmd.cond)
       if (cmd.table == FRQ_TABLE
          || (cmd.table == FRQ_LIMIT && n_categories <= cmd.limit))
        switch (cmd.cond)
@@ -547,17 +575,16 @@ postcalc (void *aux UNUSED)
        {
          struct chart ch;
          double d[frq_n_stats];
        {
          struct chart ch;
          double d[frq_n_stats];
-         struct frequencies_proc *frq = &v->p.frq;
          
          struct normal_curve norm;
          
          struct normal_curve norm;
-         norm.N = frq->tab.total_cases ;
+         norm.N = vf->tab.total_cases;
 
          calc_stats(v,d);
          norm.mean = d[frq_mean];
          norm.stddev = d[frq_stddev];
 
          chart_initialise(&ch);
 
          calc_stats(v,d);
          norm.mean = d[frq_mean];
          norm.stddev = d[frq_stddev];
 
          chart_initialise(&ch);
-         draw_histogram(&ch, v_variables[i], "HISTOGRAM",&norm,normal);
+         draw_histogram(&ch, v_variables[i], ft, "HISTOGRAM",&norm,normal);
          chart_finalise(&ch);
        }
 
          chart_finalise(&ch);
        }
 
@@ -568,7 +595,7 @@ postcalc (void *aux UNUSED)
 
          chart_initialise(&ch);
          
 
          chart_initialise(&ch);
          
-         draw_piechart(&ch, v_variables[i]);
+         draw_piechart(&ch, v_variables[i], ft);
 
          chart_finalise(&ch);
        }
 
          chart_finalise(&ch);
        }
@@ -624,9 +651,9 @@ postprocess_freq_tab (struct variable *v)
   struct freq *freqs, *f;
   size_t i;
 
   struct freq *freqs, *f;
   size_t i;
 
-  assert (v->p.frq.tab.mode == FRQM_GENERAL);
+  ft = &get_var_freqs (v)->tab;
+  assert (ft->mode == FRQM_GENERAL);
   compare = get_freq_comparator (cmd.sort, v->type);
   compare = get_freq_comparator (cmd.sort, v->type);
-  ft = &v->p.frq.tab;
 
   /* Extract data from hash table. */
   count = hsh_count (ft->data);
 
   /* Extract data from hash table. */
   count = hsh_count (ft->data);
@@ -672,9 +699,10 @@ postprocess_freq_tab (struct variable *v)
 static void
 cleanup_freq_tab (struct variable *v)
 {
 static void
 cleanup_freq_tab (struct variable *v)
 {
-  assert (v->p.frq.tab.mode == FRQM_GENERAL);
-  free (v->p.frq.tab.valid);
-  hsh_destroy (v->p.frq.tab.data);
+  struct freq_tab *ft = &get_var_freqs (v)->tab;
+  assert (ft->mode == FRQM_GENERAL);
+  free (ft->valid);
+  hsh_destroy (ft->data);
 }
 
 /* Parses the VARIABLES subcommand, adding to
 }
 
 /* Parses the VARIABLES subcommand, adding to
@@ -697,9 +725,6 @@ frq_custom_variables (struct cmd_frequencies *cmd UNUSED)
                        PV_APPEND | PV_NO_SCRATCH))
     return 0;
 
                        PV_APPEND | PV_NO_SCRATCH))
     return 0;
 
-  for (i = old_n_variables; i < n_variables; i++)
-    v_variables[i]->p.frq.tab.mode = FRQM_GENERAL;
-
   if (!lex_match ('('))
     mode = FRQM_GENERAL;
   else
   if (!lex_match ('('))
     mode = FRQM_GENERAL;
   else
@@ -728,42 +753,40 @@ frq_custom_variables (struct cmd_frequencies *cmd UNUSED)
   for (i = old_n_variables; i < n_variables; i++)
     {
       struct variable *v = v_variables[i];
   for (i = old_n_variables; i < n_variables; i++)
     {
       struct variable *v = v_variables[i];
+      struct var_freqs *vf;
 
 
-      if (v->p.frq.used != 0)
+      if (v->aux != NULL)
        {
          msg (SE, _("Variable %s specified multiple times on VARIABLES "
                     "subcommand."), v->name);
          return 0;
        }
        {
          msg (SE, _("Variable %s specified multiple times on VARIABLES "
                     "subcommand."), v->name);
          return 0;
        }
-      
-      v->p.frq.used = 1;               /* Used simply as a marker. */
-
-      v->p.frq.tab.valid = v->p.frq.tab.missing = NULL;
+      if (mode == FRQM_INTEGER && v->type != NUMERIC)
+        {
+          msg (SE, _("Integer mode specified, but %s is not a numeric "
+                     "variable."), v->name);
+          return 0;
+        }
 
 
+      vf = var_attach_aux (v, xmalloc (sizeof *vf), var_dtor_free);
+      vf->tab.mode = mode;
+      vf->tab.valid = vf->tab.missing = NULL;
       if (mode == FRQM_INTEGER)
        {
       if (mode == FRQM_INTEGER)
        {
-         if (v->type != NUMERIC)
-           {
-             msg (SE, _("Integer mode specified, but %s is not a numeric "
-                        "variable."), v->name);
-             return 0;
-           }
-         
-         v->p.frq.tab.min = min;
-         v->p.frq.tab.max = max;
-         v->p.frq.tab.vector = pool_alloc (int_pool,
-                                           sizeof (struct freq) * (max - min + 1));
+         vf->tab.min = min;
+         vf->tab.max = max;
+         vf->tab.vector = pool_alloc (int_pool,
+                                       sizeof (struct freq) * (max - min + 1));
        }
       else
        }
       else
-       v->p.frq.tab.vector = NULL;
-
-      v->p.frq.n_groups = 0;
-      v->p.frq.groups = NULL;
+       vf->tab.vector = NULL;
+      vf->n_groups = 0;
+      vf->groups = NULL;
     }
   return 1;
 }
 
     }
   return 1;
 }
 
-/* Parses the GROUPED subcommand, setting the frq.{n_grouped,grouped}
+/* Parses the GROUPED subcommand, setting the n_grouped, grouped
    fields of specified variables. */
 static int
 frq_custom_grouped (struct cmd_frequencies *cmd UNUSED)
    fields of specified variables. */
 static int
 frq_custom_grouped (struct cmd_frequencies *cmd UNUSED)
@@ -817,19 +840,22 @@ frq_custom_grouped (struct cmd_frequencies *cmd UNUSED)
           }
 
        for (i = 0; i < n; i++)
           }
 
        for (i = 0; i < n; i++)
-         {
-           if (v[i]->p.frq.used == 0)
-             msg (SE, _("Variables %s specified on GROUPED but not on "
-                  "VARIABLES."), v[i]->name);
-           if (v[i]->p.frq.groups != NULL)
-             msg (SE, _("Variables %s specified multiple times on GROUPED "
-                  "subcommand."), v[i]->name);
-           else
-             {
-               v[i]->p.frq.n_groups = nl;
-               v[i]->p.frq.groups = dl;
-             }
-         }
+          if (v[i]->aux == NULL)
+            msg (SE, _("Variables %s specified on GROUPED but not on "
+                       "VARIABLES."), v[i]->name);
+          else 
+            {
+              struct var_freqs *vf = get_var_freqs (v[i]);
+                
+              if (vf->groups != NULL)
+                msg (SE, _("Variables %s specified multiple times on GROUPED "
+                           "subcommand."), v[i]->name);
+              else
+                {
+                  vf->n_groups = nl;
+                  vf->groups = dl;
+                }
+            }
        free (v);
        if (!lex_match ('/'))
          break;
        free (v);
        if (!lex_match ('/'))
          break;
@@ -1032,9 +1058,10 @@ full_dim (struct tab_table *t, struct outp_driver *d)
 
 /* Displays a full frequency table for variable V. */
 static void
 
 /* Displays a full frequency table for variable V. */
 static void
-dump_full (struct variable * v)
+dump_full (struct variable *v)
 {
   int n_categories;
 {
   int n_categories;
+  struct freq_tab *ft;
   struct freq *f;
   struct tab_table *t;
   int r;
   struct freq *f;
   struct tab_table *t;
   int r;
@@ -1067,7 +1094,8 @@ dump_full (struct variable * v)
 
   int lab = cmd.labels == FRQ_LABELS;
 
 
   int lab = cmd.labels == FRQ_LABELS;
 
-  n_categories = v->p.frq.tab.n_valid + v->p.frq.tab.n_missing;
+  ft = &get_var_freqs (v)->tab;
+  n_categories = ft->n_valid + ft->n_missing;
   t = tab_create (5 + lab, n_categories + 3, 0);
   tab_headers (t, 0, 0, 2, 0);
   tab_dim (t, full_dim);
   t = tab_create (5 + lab, n_categories + 3, 0);
   tab_headers (t, 0, 0, 2, 0);
   tab_dim (t, full_dim);
@@ -1079,14 +1107,14 @@ dump_full (struct variable * v)
                  TAB_CENTER | TAT_TITLE, gettext (p->s));
 
   r = 2;
                  TAB_CENTER | TAT_TITLE, gettext (p->s));
 
   r = 2;
-  for (f = v->p.frq.tab.valid; f < v->p.frq.tab.missing; f++)
+  for (f = ft->valid; f < ft->missing; f++)
     {
       double percent, valid_percent;
 
       cum_freq += f->c;
 
     {
       double percent, valid_percent;
 
       cum_freq += f->c;
 
-      percent = f->c / v->p.frq.tab.total_cases * 100.0;
-      valid_percent = f->c / v->p.frq.tab.valid_cases * 100.0;
+      percent = f->c / ft->total_cases * 100.0;
+      valid_percent = f->c / ft->valid_cases * 100.0;
       cum_total += valid_percent;
 
       if (lab)
       cum_total += valid_percent;
 
       if (lab)
@@ -1103,7 +1131,7 @@ dump_full (struct variable * v)
       tab_float (t, 4 + lab, r, TAB_NONE, cum_total, 5, 1);
       r++;
     }
       tab_float (t, 4 + lab, r, TAB_NONE, cum_total, 5, 1);
       r++;
     }
-  for (; f < &v->p.frq.tab.valid[n_categories]; f++)
+  for (; f < &ft->valid[n_categories]; f++)
     {
       cum_freq += f->c;
 
     {
       cum_freq += f->c;
 
@@ -1117,7 +1145,7 @@ dump_full (struct variable * v)
       tab_value (t, 0 + lab, r, TAB_NONE, &f->v, &v->print);
       tab_float (t, 1 + lab, r, TAB_NONE, f->c, 8, 0);
       tab_float (t, 2 + lab, r, TAB_NONE,
       tab_value (t, 0 + lab, r, TAB_NONE, &f->v, &v->print);
       tab_float (t, 1 + lab, r, TAB_NONE, f->c, 8, 0);
       tab_float (t, 2 + lab, r, TAB_NONE,
-                    f->c / v->p.frq.tab.total_cases * 100.0, 5, 1);
+                    f->c / ft->total_cases * 100.0, 5, 1);
       tab_text (t, 3 + lab, r, TAB_NONE, _("Missing"));
       r++;
     }
       tab_text (t, 3 + lab, r, TAB_NONE, _("Missing"));
       r++;
     }
@@ -1159,15 +1187,17 @@ condensed_dim (struct tab_table *t, struct outp_driver *d)
 
 /* Display condensed frequency table for variable V. */
 static void
 
 /* Display condensed frequency table for variable V. */
 static void
-dump_condensed (struct variable * v)
+dump_condensed (struct variable *v)
 {
   int n_categories;
 {
   int n_categories;
+  struct freq_tab *ft;
   struct freq *f;
   struct tab_table *t;
   int r;
   double cum_total = 0.0;
 
   struct freq *f;
   struct tab_table *t;
   int r;
   double cum_total = 0.0;
 
-  n_categories = v->p.frq.tab.n_valid + v->p.frq.tab.n_missing;
+  ft = &get_var_freqs (v)->tab;
+  n_categories = ft->n_valid + ft->n_missing;
   t = tab_create (4, n_categories + 2, 0);
 
   tab_headers (t, 0, 0, 2, 0);
   t = tab_create (4, n_categories + 2, 0);
 
   tab_headers (t, 0, 0, 2, 0);
@@ -1179,12 +1209,12 @@ dump_condensed (struct variable * v)
   tab_dim (t, condensed_dim);
 
   r = 2;
   tab_dim (t, condensed_dim);
 
   r = 2;
-  for (f = v->p.frq.tab.valid; f < v->p.frq.tab.missing; f++)
+  for (f = ft->valid; f < ft->missing; f++)
     {
       double percent;
 
     {
       double percent;
 
-      percent = f->c / v->p.frq.tab.total_cases * 100.0;
-      cum_total += f->c / v->p.frq.tab.valid_cases * 100.0;
+      percent = f->c / ft->total_cases * 100.0;
+      cum_total += f->c / ft->valid_cases * 100.0;
 
       tab_value (t, 0, r, TAB_NONE, &f->v, &v->print);
       tab_float (t, 1, r, TAB_NONE, f->c, 8, 0);
 
       tab_value (t, 0, r, TAB_NONE, &f->v, &v->print);
       tab_float (t, 1, r, TAB_NONE, f->c, 8, 0);
@@ -1192,12 +1222,12 @@ dump_condensed (struct variable * v)
       tab_float (t, 3, r, TAB_NONE, cum_total, 3, 0);
       r++;
     }
       tab_float (t, 3, r, TAB_NONE, cum_total, 3, 0);
       r++;
     }
-  for (; f < &v->p.frq.tab.valid[n_categories]; f++)
+  for (; f < &ft->valid[n_categories]; f++)
     {
       tab_value (t, 0, r, TAB_NONE, &f->v, &v->print);
       tab_float (t, 1, r, TAB_NONE, f->c, 8, 0);
       tab_float (t, 2, r, TAB_NONE,
     {
       tab_value (t, 0, r, TAB_NONE, &f->v, &v->print);
       tab_float (t, 1, r, TAB_NONE, f->c, 8, 0);
       tab_float (t, 2, r, TAB_NONE,
-                f->c / v->p.frq.tab.total_cases * 100.0, 3, 0);
+                f->c / ft->total_cases * 100.0, 3, 0);
       r++;
     }
 
       r++;
     }
 
@@ -1215,9 +1245,10 @@ dump_condensed (struct variable * v)
 /* Calculates all the pertinent statistics for variable V, putting
    them in array D[].  FIXME: This could be made much more optimal. */
 static void
 /* Calculates all the pertinent statistics for variable V, putting
    them in array D[].  FIXME: This could be made much more optimal. */
 static void
-calc_stats (struct variable * v, double d[frq_n_stats])
+calc_stats (struct variable *v, double d[frq_n_stats])
 {
 {
-  double W = v->p.frq.tab.valid_cases;
+  struct freq_tab *ft = &get_var_freqs (v)->tab;
+  double W = ft->valid_cases;
   struct moments *m;
   struct freq *f=0; 
   int most_often;
   struct moments *m;
   struct freq *f=0; 
   int most_often;
@@ -1255,10 +1286,10 @@ calc_stats (struct variable * v, double d[frq_n_stats])
     }
 
   rank = 0;
     }
 
   rank = 0;
-  for (idx = 0; idx < v->p.frq.tab.n_valid; ++idx)
+  for (idx = 0; idx < ft->n_valid; ++idx)
     {
       static double prev_value = SYSMIS;
     {
       static double prev_value = SYSMIS;
-      f = &v->p.frq.tab.valid[idx]; 
+      f = &ft->valid[idx]; 
       rank += f->c ;
       for (i = 0; i < n_percentiles; i++) 
         {
       rank += f->c ;
       for (i = 0; i < n_percentiles; i++) 
         {
@@ -1267,10 +1298,10 @@ calc_stats (struct variable * v, double d[frq_n_stats])
 
          if ( get_algorithm() != COMPATIBLE ) 
            tp = 
 
          if ( get_algorithm() != COMPATIBLE ) 
            tp = 
-             (v->p.frq.tab.valid_cases - 1) *  percentiles[i].p;
+             (ft->valid_cases - 1) *  percentiles[i].p;
          else
            tp = 
          else
            tp = 
-             (v->p.frq.tab.valid_cases + 1) *  percentiles[i].p - 1;
+             (ft->valid_cases + 1) *  percentiles[i].p - 1;
 
          if ( percentiles[i].flag ) 
            {
 
          if ( percentiles[i].flag ) 
            {
@@ -1312,17 +1343,17 @@ calc_stats (struct variable * v, double d[frq_n_stats])
 
   for (i = 0; i < n_percentiles; i++) 
     {
 
   for (i = 0; i < n_percentiles; i++) 
     {
-      struct freq_tab *ft = &v->p.frq.tab;
+      struct freq_tab *ft = &get_var_freqs (v)->tab;
       double s;
 
       double dummy;
       if ( get_algorithm() != COMPATIBLE ) 
        {
       double s;
 
       double dummy;
       if ( get_algorithm() != COMPATIBLE ) 
        {
-         s = modf((ft->valid_cases - 1) *  percentiles[i].p , &dummy);
+         s = modf((ft->valid_cases - 1) * percentiles[i].p , &dummy);
        }
       else
        {
        }
       else
        {
-         s = modf((ft->valid_cases + 1) *  percentiles[i].p -1, &dummy);
+         s = modf((ft->valid_cases + 1) * percentiles[i].p -1, &dummy);
        }
 
       percentiles[i].value = percentiles[i].x1 + 
        }
 
       percentiles[i].value = percentiles[i].x1 + 
@@ -1336,7 +1367,7 @@ calc_stats (struct variable * v, double d[frq_n_stats])
   /* Calculate the mode. */
   most_often = -1;
   X_mode = SYSMIS;
   /* Calculate the mode. */
   most_often = -1;
   X_mode = SYSMIS;
-  for (f = v->p.frq.tab.valid; f < v->p.frq.tab.missing; f++)
+  for (f = ft->valid; f < ft->missing; f++)
     {
       if (most_often < f->c) 
         {
     {
       if (most_often < f->c) 
         {
@@ -1353,17 +1384,17 @@ calc_stats (struct variable * v, double d[frq_n_stats])
 
   /* Calculate moments. */
   m = moments_create (MOMENT_KURTOSIS);
 
   /* Calculate moments. */
   m = moments_create (MOMENT_KURTOSIS);
-  for (f = v->p.frq.tab.valid; f < v->p.frq.tab.missing; f++)
+  for (f = ft->valid; f < ft->missing; f++)
     moments_pass_one (m, f->v.f, f->c);
     moments_pass_one (m, f->v.f, f->c);
-  for (f = v->p.frq.tab.valid; f < v->p.frq.tab.missing; f++)
+  for (f = ft->valid; f < ft->missing; f++)
     moments_pass_two (m, f->v.f, f->c);
   moments_calculate (m, NULL, &d[frq_mean], &d[frq_variance],
                      &d[frq_skew], &d[frq_kurt]);
   moments_destroy (m);
                      
   /* Formulas below are taken from _SPSS Statistical Algorithms_. */
     moments_pass_two (m, f->v.f, f->c);
   moments_calculate (m, NULL, &d[frq_mean], &d[frq_variance],
                      &d[frq_skew], &d[frq_kurt]);
   moments_destroy (m);
                      
   /* Formulas below are taken from _SPSS Statistical Algorithms_. */
-  d[frq_min] = v->p.frq.tab.valid[0].v.f;
-  d[frq_max] = v->p.frq.tab.valid[v->p.frq.tab.n_valid - 1].v.f;
+  d[frq_min] = ft->valid[0].v.f;
+  d[frq_max] = ft->valid[ft->n_valid - 1].v.f;
   d[frq_mode] = X_mode;
   d[frq_range] = d[frq_max] - d[frq_min];
   d[frq_median] = *median_value;
   d[frq_mode] = X_mode;
   d[frq_range] = d[frq_max] - d[frq_min];
   d[frq_median] = *median_value;
@@ -1376,8 +1407,9 @@ calc_stats (struct variable * v, double d[frq_n_stats])
 
 /* Displays a table of all the statistics requested for variable V. */
 static void
 
 /* Displays a table of all the statistics requested for variable V. */
 static void
-dump_statistics (struct variable * v, int show_varname)
+dump_statistics (struct variable *v, int show_varname)
 {
 {
+  struct freq_tab *ft;
   double stat_value[frq_n_stats];
   struct tab_table *t;
   int i, r;
   double stat_value[frq_n_stats];
   struct tab_table *t;
   int i, r;
@@ -1389,7 +1421,8 @@ dump_statistics (struct variable * v, int show_varname)
 
   if (v->type == ALPHA)
     return;
 
   if (v->type == ALPHA)
     return;
-  if (v->p.frq.tab.n_valid == 0)
+  ft = &get_var_freqs (v)->tab;
+  if (ft->n_valid == 0)
     {
       msg (SW, _("No valid data for variable %s; statistics not displayed."),
           v->name);
     {
       msg (SW, _("No valid data for variable %s; statistics not displayed."),
           v->name);
@@ -1421,9 +1454,8 @@ dump_statistics (struct variable * v, int show_varname)
   tab_text (t, 1, 0, TAB_LEFT | TAT_TITLE, _("Valid"));
   tab_text (t, 1, 1, TAB_LEFT | TAT_TITLE, _("Missing"));
 
   tab_text (t, 1, 0, TAB_LEFT | TAT_TITLE, _("Valid"));
   tab_text (t, 1, 1, TAB_LEFT | TAT_TITLE, _("Missing"));
 
-  tab_float(t, 2, 0, TAB_NONE, v->p.frq.tab.valid_cases, 11, 0);
-  tab_float(t, 2, 1, TAB_NONE, 
-           v->p.frq.tab.total_cases - v->p.frq.tab.valid_cases, 11, 0);
+  tab_float(t, 2, 0, TAB_NONE, ft->valid_cases, 11, 0);
+  tab_float(t, 2, 1, TAB_NONE, ft->total_cases - ft->valid_cases, 11, 0);
 
 
   for (i = 0; i < n_explicit_percentiles; i++, r++) 
 
 
   for (i = 0; i < n_explicit_percentiles; i++, r++) 
index dc16d7a5372328482cd71d210ad9ff6a00872b41..8f98dea460821bc30578e1153cdd1eb45390ce26 100644 (file)
--- a/src/get.c
+++ b/src/get.c
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "file-handle.h"
 #include "hash.h"
 #include "lexer.h"
 #include "misc.h"
 #include "error.h"
 #include "file-handle.h"
 #include "hash.h"
 #include "lexer.h"
 #include "misc.h"
-#include "pfm.h"
+#include "pfm-read.h"
+#include "pfm-write.h"
 #include "settings.h"
 #include "settings.h"
-#include "sfm.h"
+#include "sfm-read.h"
+#include "sfm-write.h"
 #include "str.h"
 #include "value-labels.h"
 #include "var.h"
 #include "str.h"
 #include "value-labels.h"
 #include "var.h"
 
 #include "debug-print.h"
 
 
 #include "debug-print.h"
 
-/* GET or IMPORT input program. */
-struct get_pgm 
+/* Rearranging and reducing a dictionary. */
+static void start_case_map (struct dictionary *);
+static struct case_map *finish_case_map (struct dictionary *);
+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 
   {
   {
-    struct file_handle *handle; /* File to GET or IMPORT from. */
-    size_t case_size;           /* Case size in bytes. */
+    OP_READ,    /* GET or IMPORT. */
+    OP_SAVE,    /* SAVE or XSAVE. */
+    OP_EXPORT,  /* EXPORT. */
+    OP_MATCH    /* MATCH FILES. */
   };
 
   };
 
-/* XSAVE transformation (and related SAVE, EXPORT procedures). */
-struct save_trns
+static int trim_dictionary (struct dictionary *,
+                            enum operation, int *compress);
+\f
+/* GET input program. */
+struct get_pgm 
   {
   {
-    struct trns_header h;
-    struct file_handle *f;     /* Associated system file. */
-    int nvar;                  /* Number of variables. */
-    struct variable **var;      /* Variables. */
-    flt64 *case_buf;           /* Case transfer buffer. */
+    struct sfm_reader *reader;  /* System file reader. */
+    struct case_map *map;       /* Map from system file to active file dict. */
+    struct ccase bounce;        /* Bounce buffer. */
   };
 
   };
 
-/* Options bits set by trim_dictionary(). */
-#define GTSV_OPT_COMPRESSED    001     /* Compression; (X)SAVE only. */
-#define GTSV_OPT_SAVE          002     /* The SAVE/XSAVE/EXPORT procedures. */
-#define GTSV_OPT_MATCH_FILES   004     /* The MATCH FILES procedure. */
-#define GTSV_OPT_NONE          0
-
-static int trim_dictionary (struct dictionary * dict, int *options);
-static int save_write_case_func (struct ccase *, void *);
-static trns_proc_func save_trns_proc;
-static trns_free_func save_trns_free;
+static void get_pgm_free (struct get_pgm *);
 
 /* Parses the GET command. */
 int
 cmd_get (void)
 {
 
 /* Parses the GET command. */
 int
 cmd_get (void)
 {
-  struct file_handle *handle;
-  struct dictionary *dict;
-  struct get_pgm *pgm;
-  int options = GTSV_OPT_NONE;
+  struct get_pgm *pgm = NULL;
+  struct file_handle *fh;
+  struct dictionary *dict = NULL;
+
+  pgm = xmalloc (sizeof *pgm);
+  pgm->reader = NULL;
+  pgm->map = NULL;
+  case_nullify (&pgm->bounce);
 
   discard_variables ();
 
   lex_match ('/');
   if (lex_match_id ("FILE"))
     lex_match ('=');
 
   discard_variables ();
 
   lex_match ('/');
   if (lex_match_id ("FILE"))
     lex_match ('=');
+  fh = fh_parse ();
+  if (fh == NULL)
+    goto error;
 
 
-  handle = fh_parse_file_handle ();
-  if (handle == NULL)
-    return CMD_FAILURE;
-
-  dict = sfm_read_dictionary (handle, NULL);
-  if (dict == NULL)
-    return CMD_FAILURE;
-
-  if (0 == trim_dictionary (dict, &options))
-    {
-      fh_close_handle (handle);
-      return CMD_FAILURE;
-    }
+  pgm->reader = sfm_open_reader (fh, &dict, NULL);
+  if (pgm->reader == NULL)
+    goto error;
+  case_create (&pgm->bounce, dict_get_next_value_idx (dict));
 
 
-  dict_compact_values (dict);
+  start_case_map (dict);
+  if (!trim_dictionary (dict, OP_READ, NULL))
+    goto error;
+  pgm->map = finish_case_map (dict);
 
   dict_destroy (default_dict);
   default_dict = dict;
 
 
   dict_destroy (default_dict);
   default_dict = dict;
 
-  pgm = xmalloc (sizeof *pgm);
-  pgm->handle = handle;
-  pgm->case_size = dict_get_case_size (default_dict);
   vfm_source = create_case_source (&get_source_class, default_dict, pgm);
 
   return CMD_SUCCESS;
   vfm_source = create_case_source (&get_source_class, default_dict, pgm);
 
   return CMD_SUCCESS;
+
+ error:
+  get_pgm_free (pgm);
+  if (dict != NULL)
+    dict_destroy (dict);
+  return CMD_FAILURE;
 }
 
 }
 
-/* SAVE or XSAVE command? */
-enum save_cmd 
-  {
-    CMD_SAVE,
-    CMD_XSAVE
-  };
+/* Frees a struct get_pgm. */
+static void
+get_pgm_free (struct get_pgm *pgm) 
+{
+  if (pgm != NULL) 
+    {
+      sfm_close_reader (pgm->reader);
+      destroy_case_map (pgm->map);
+      case_destroy (&pgm->bounce);
+      free (pgm);
+    }
+}
 
 
-/* Parses the SAVE and XSAVE commands.  */
-static int
-cmd_save_internal (enum save_cmd save_cmd)
+/* Clears internal state related to GET input procedure. */
+static void
+get_source_destroy (struct case_source *source)
 {
 {
-  struct file_handle *handle;
-  struct dictionary *dict;
-  int options = GTSV_OPT_SAVE;
+  struct get_pgm *pgm = source->aux;
+  get_pgm_free (pgm);
+}
 
 
-  struct save_trns *t;
-  struct sfm_write_info inf;
+/* 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)
+{
+  struct get_pgm *pgm = source->aux;
+  int ok;
 
 
-  int i;
+  do
+    {
+      if (pgm->map == NULL)
+        ok = sfm_read_case (pgm->reader, c);
+      else
+        {
+          ok = sfm_read_case (pgm->reader, &pgm->bounce);
+          if (ok)
+            map_case (pgm->map, &pgm->bounce, c);
+        }
 
 
-  lex_match ('/');
-  if (lex_match_id ("OUTFILE"))
-    lex_match ('=');
+      if (ok)
+        ok = write_case (wc_data);
+    }
+  while (ok);
+}
 
 
-  handle = fh_parse_file_handle ();
-  if (handle == NULL)
-    return CMD_FAILURE;
+const struct case_source_class get_source_class =
+  {
+    "GET",
+    NULL,
+    get_source_read,
+    get_source_destroy,
+  };
+\f
+/* XSAVE transformation and SAVE procedure. */
+struct save_trns
+  {
+    struct trns_header h;
+    struct sfm_writer *writer;  /* System file writer. */
+    struct case_map *map;       /* Map from active file to system file dict. */
+    struct ccase bounce;        /* Bounce buffer. */
+  };
 
 
-  dict = dict_clone (default_dict);
-  for (i = 0; i < dict_get_var_cnt (dict); i++) 
-    dict_get_var (dict, i)->aux = dict_get_var (default_dict, i);
-  if (0 == trim_dictionary (dict, &options))
-    {
-      fh_close_handle (handle);
-      return CMD_FAILURE;
-    }
+static int save_write_case_func (struct ccase *, void *);
+static trns_proc_func save_trns_proc;
+static trns_free_func save_trns_free;
 
 
-  /* Write dictionary. */
-  inf.h = handle;
-  inf.dict = dict;
-  inf.compress = !!(options & GTSV_OPT_COMPRESSED);
-  if (!sfm_write_dictionary (&inf))
-    {
-      dict_destroy (dict);
-      fh_close_handle (handle);
-      return CMD_FAILURE;
-    }
+/* Parses the SAVE or XSAVE command
+   and returns the parsed transformation. */
+static struct save_trns *
+cmd_save_internal (void)
+{
+  struct file_handle *fh;
+  struct dictionary *dict = NULL;
+  struct save_trns *t = NULL;
+  int compress = get_scompression ();
 
 
-  /* Fill in transformation structure. */
   t = xmalloc (sizeof *t);
   t->h.proc = save_trns_proc;
   t->h.free = save_trns_free;
   t = xmalloc (sizeof *t);
   t->h.proc = save_trns_proc;
   t->h.free = save_trns_free;
-  t->f = handle;
-  t->nvar = dict_get_var_cnt (dict);
-  t->var = xmalloc (sizeof *t->var * t->nvar);
-  for (i = 0; i < t->nvar; i++)
-    t->var[i] = dict_get_var (dict, i)->aux;
-  t->case_buf = xmalloc (sizeof *t->case_buf * inf.case_size);
+  t->writer = NULL;
+  t->map = NULL;
+  case_nullify (&t->bounce);
+  
+  lex_match ('/');
+  if (lex_match_id ("OUTFILE"))
+    lex_match ('=');
+  fh = fh_parse ();
+  if (fh == NULL)
+    goto error;
+
+  dict = dict_clone (default_dict);
+  start_case_map (dict);
+  if (!trim_dictionary (dict, OP_SAVE, &compress))
+    goto error;
+  t->map = finish_case_map (dict);
+  if (t->map != NULL)
+    case_create (&t->bounce, dict_get_next_value_idx (dict));
+
+  t->writer = sfm_open_writer (fh, dict, compress);
+  if (t->writer == NULL)
+    goto error;
+
   dict_destroy (dict);
 
   dict_destroy (dict);
 
-  if (save_cmd == CMD_SAVE)
-    {
-      procedure (save_write_case_func, t);
-      save_trns_free (&t->h);
-    }
-  else 
-    {
-      assert (save_cmd == CMD_XSAVE);
-      add_transformation (&t->h); 
-    }
+  return t;
 
 
-  return CMD_SUCCESS;
+ error:
+  assert (t != NULL);
+  dict_destroy (dict);
+  save_trns_free (&t->h);
+  return NULL;
 }
 
 /* Parses and performs the SAVE procedure. */
 int
 cmd_save (void)
 {
 }
 
 /* Parses and performs the SAVE procedure. */
 int
 cmd_save (void)
 {
-  return cmd_save_internal (CMD_SAVE);
+  struct save_trns *t = cmd_save_internal ();
+  if (t != NULL) 
+    {
+      procedure (save_write_case_func, t);
+      save_trns_free (&t->h);
+      return CMD_SUCCESS;
+    }
+  else
+    return CMD_FAILURE;
 }
 
 /* Parses the XSAVE transformation command. */
 int
 cmd_xsave (void)
 {
 }
 
 /* Parses the XSAVE transformation command. */
 int
 cmd_xsave (void)
 {
-  return cmd_save_internal (CMD_XSAVE);
+  struct save_trns *t = cmd_save_internal ();
+  if (t != NULL) 
+    {
+      add_transformation (&t->h);
+      return CMD_SUCCESS; 
+    }
+  else
+    return CMD_FAILURE;
 }
 
 /* Writes the given C to the file specified by T. */
 static void
 do_write_case (struct save_trns *t, struct ccase *c) 
 {
 }
 
 /* Writes the given C to the file specified by T. */
 static void
 do_write_case (struct save_trns *t, struct ccase *c) 
 {
-  flt64 *p = t->case_buf;
-  int i;
-
-  for (i = 0; i < t->nvar; i++)
+  if (t->map == NULL)
+    sfm_write_case (t->writer, c);
+  else 
     {
     {
-      struct variable *v = t->var[i];
-      if (v->type == NUMERIC)
-       {
-         double src = case_num (c, v->fv);
-         if (src == SYSMIS)
-           *p++ = -FLT64_MAX;
-         else
-           *p++ = src;
-       }
-      else
-       {
-         memcpy (p, case_str (c, v->fv), v->width);
-         memset (&((char *) p)[v->width], ' ',
-                 REM_RND_UP (v->width, sizeof *p));
-         p += DIV_RND_UP (v->width, sizeof *p);
-       }
+      map_case (t->map, c, &t->bounce);
+      sfm_write_case (t->writer, &t->bounce);
     }
     }
-
-  sfm_write_case (t->f, t->case_buf, p - t->case_buf);
 }
 
 /* Writes case C to the system file specified on SAVE. */
 }
 
 /* Writes case C to the system file specified on SAVE. */
@@ -246,33 +294,39 @@ save_trns_proc (struct trns_header *h, struct ccase *c, int case_num UNUSED)
 
 /* Frees a SAVE transformation. */
 static void
 
 /* Frees a SAVE transformation. */
 static void
-save_trns_free (struct trns_header *pt)
+save_trns_free (struct trns_header *t_)
 {
 {
-  struct save_trns *t = (struct save_trns *) pt;
+  struct save_trns *t = (struct save_trns *) t_;
 
 
-  fh_close_handle (t->f);
-  free (t->var);
-  free (t->case_buf);
-  free (t);
+  if (t != NULL) 
+    {
+      sfm_close_writer (t->writer);
+      destroy_case_map (t->map);
+      case_destroy (&t->bounce);
+    }
 }
 
 }
 
-static int rename_variables (struct dictionary * dict);
+static int rename_variables (struct dictionary *dict);
+
+/* Commands that read and write system files share a great deal
+   of common syntactic structure for rearranging and dropping
+   variables.  This function parses this syntax and modifies DICT
+   appropriately.
 
 
-/* The GET and SAVE commands have a common structure after the
-   FILE/OUTFILE subcommand.  This function parses this structure and
-   returns nonzero on success, zero on failure.  It both reads
-   *OPTIONS, for the GTSV_OPT_SAVE bit, and writes it, for the
-   GTSV_OPT_COMPRESSED bit. */
+   OP is the operation being performed.  For operations that
+   write a system file, *COMPRESS is set to 1 if the system file
+   should be compressed, 0 otherwise.
+   
+   Returns nonzero on success, zero on failure. */
 /* FIXME: IN, FIRST, LAST, MAP. */
 /* FIXME: IN, FIRST, LAST, MAP. */
-/* FIXME?  Should we call dict_compact_values() on dict as a
-   final step? */
 static int
 static int
-trim_dictionary (struct dictionary *dict, int *options)
+trim_dictionary (struct dictionary *dict, enum operation op, int *compress)
 {
 {
+  assert ((compress != NULL) == (op == OP_SAVE));
   if (get_scompression())
   if (get_scompression())
-    *options |= GTSV_OPT_COMPRESSED;
+    *compress = 1;
 
 
-  if (*options & GTSV_OPT_SAVE)
+  if (op == OP_SAVE || op == OP_EXPORT)
     {
       /* Delete all the scratch variables. */
       struct variable **v;
     {
       /* Delete all the scratch variables. */
       struct variable **v;
@@ -288,12 +342,12 @@ trim_dictionary (struct dictionary *dict, int *options)
       free (v);
     }
   
       free (v);
     }
   
-  while ((*options & GTSV_OPT_MATCH_FILES) || lex_match ('/'))
+  while (op == OP_MATCH || lex_match ('/'))
     {
     {
-      if (!(*options & GTSV_OPT_MATCH_FILES) && lex_match_id ("COMPRESSED"))
-       *options |= GTSV_OPT_COMPRESSED;
-      else if (!(*options & GTSV_OPT_MATCH_FILES) && lex_match_id ("UNCOMPRESSED"))
-       *options &= ~GTSV_OPT_COMPRESSED;
+      if (op == OP_SAVE && lex_match_id ("COMPRESSED"))
+       *compress = 1;
+      else if (op == OP_SAVE && lex_match_id ("UNCOMPRESSED"))
+       *compress = 0;
       else if (lex_match_id ("DROP"))
        {
          struct variable **v;
       else if (lex_match_id ("DROP"))
        {
          struct variable **v;
@@ -342,8 +396,8 @@ trim_dictionary (struct dictionary *dict, int *options)
          return 0;
        }
 
          return 0;
        }
 
-      if (*options & GTSV_OPT_MATCH_FILES)
-       return 1;
+      if (op == OP_MATCH)
+        goto success;
     }
 
   if (token != '.')
     }
 
   if (token != '.')
@@ -351,13 +405,16 @@ trim_dictionary (struct dictionary *dict, int *options)
       lex_error (_("expecting end of command"));
       return 0;
     }
       lex_error (_("expecting end of command"));
       return 0;
     }
-  
+
+ success:
+  if (op != OP_MATCH)
+    dict_compact_values (dict);
   return 1;
 }
 
 /* Parses and performs the RENAME subcommand of GET and SAVE. */
 static int
   return 1;
 }
 
 /* Parses and performs the RENAME subcommand of GET and SAVE. */
 static int
-rename_variables (struct dictionary * dict)
+rename_variables (struct dictionary *dict)
 {
   int i;
 
 {
   int i;
 
@@ -444,40 +501,88 @@ done:
   return success;
 }
 \f
   return success;
 }
 \f
-/* Clears internal state related to GET input procedure. */
-static void
-get_source_destroy (struct case_source *source)
+/* EXPORT procedure. */
+struct export_proc 
+  {
+    struct pfm_writer *writer;  /* System file writer. */
+    struct case_map *map;       /* Map from active file to system file dict. */
+    struct ccase bounce;        /* Bounce buffer. */
+  };
+
+static int export_write_case_func (struct ccase *, void *);
+static void export_proc_free (struct export_proc *);
+     
+/* Parses the EXPORT command.  */
+/* FIXME: same as cmd_save_internal(). */
+int
+cmd_export (void)
 {
 {
-  struct get_pgm *pgm = source->aux;
+  struct file_handle *fh;
+  struct dictionary *dict;
+  struct export_proc *proc;
 
 
-  /* It is not necessary to destroy the dictionary because if we get
-     to this point then the dictionary is default_dict. */
-  fh_close_handle (pgm->handle);
-  free (pgm);
-}
+  proc = xmalloc (sizeof *proc);
+  proc->writer = NULL;
+  proc->map = NULL;
+  case_nullify (&proc->bounce);
 
 
-/* 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)
-{
-  struct get_pgm *pgm = source->aux;
+  lex_match ('/');
+  if (lex_match_id ("OUTFILE"))
+    lex_match ('=');
+  fh = fh_parse ();
+  if (fh == NULL)
+    return CMD_FAILURE;
 
 
-  while (sfm_read_case (pgm->handle, c, default_dict)
-        && write_case (wc_data))
-    ;
+  dict = dict_clone (default_dict);
+  start_case_map (dict);
+  if (!trim_dictionary (dict, OP_EXPORT, NULL))
+    goto error;
+  proc->map = finish_case_map (dict);
+  if (proc->map != NULL)
+    case_create (&proc->bounce, dict_get_next_value_idx (dict));
+
+  proc->writer = pfm_open_writer (fh, dict);
+  if (proc->writer == NULL)
+    goto error;
+  
+  dict_destroy (dict);
+
+  procedure (export_write_case_func, proc);
+  export_proc_free (proc);
+
+  return CMD_SUCCESS;
+
+ error:
+  dict_destroy (dict);
+  export_proc_free (proc);
+  return CMD_FAILURE;
 }
 
 }
 
-const struct case_source_class get_source_class =
-  {
-    "GET",
-    NULL,
-    get_source_read,
-    get_source_destroy,
-  };
+/* Writes case C to the EXPORT file. */
+static int
+export_write_case_func (struct ccase *c, void *aux) 
+{
+  struct export_proc *proc = aux;
+  if (proc->map == NULL)
+    pfm_write_case (proc->writer, c);
+  else 
+    {
+      map_case (proc->map, c, &proc->bounce);
+      pfm_write_case (proc->writer, &proc->bounce);
+    }
+  return 1;
+}
 
 
+static void
+export_proc_free (struct export_proc *proc) 
+{
+  if (proc != NULL) 
+    {
+      pfm_close_writer (proc->writer);
+      destroy_case_map (proc->map);
+      case_destroy (&proc->bounce);
+    }
+}
 \f
 /* MATCH FILES. */
 
 \f
 /* MATCH FILES. */
 
@@ -499,7 +604,8 @@ struct mtf_file
     
     int type;                  /* One of MTF_*. */
     struct variable **by;      /* List of BY variables for this file. */
     
     int type;                  /* One of MTF_*. */
     struct variable **by;      /* List of BY variables for this file. */
-    struct file_handle *handle;        /* File handle for the file. */
+    struct file_handle *handle; /* File handle. */
+    struct sfm_reader *reader;  /* System file reader. */
     struct dictionary *dict;   /* Dictionary from system file. */
     char in[9];                        /* Name of the variable from IN=. */
     char first[9], last[9];    /* Name of the variables from FIRST=, LAST=. */
     struct dictionary *dict;   /* Dictionary from system file. */
     char in[9];                        /* Name of the variable from IN=. */
     char first[9], last[9];    /* Name of the variables from FIRST=, LAST=. */
@@ -534,12 +640,16 @@ static int mtf_processing (struct ccase *, void *);
 
 static char *var_type_description (struct variable *);
 
 
 static char *var_type_description (struct variable *);
 
+static void set_master (struct variable *, struct variable *master);
+static struct variable *get_master (struct variable *);
+
 /* Parse and execute the MATCH FILES command. */
 int
 cmd_match_files (void)
 {
   struct mtf_proc mtf;
   struct mtf_file *first_table = NULL;
 /* Parse and execute the MATCH FILES command. */
 int
 cmd_match_files (void)
 {
   struct mtf_proc mtf;
   struct mtf_file *first_table = NULL;
+  struct mtf_file *iter;
   
   int seen = 0;
   
   
   int seen = 0;
   
@@ -562,29 +672,24 @@ cmd_match_files (void)
          if (seen & 1)
            {
              msg (SE, _("The BY subcommand may be given once at most."));
          if (seen & 1)
            {
              msg (SE, _("The BY subcommand may be given once at most."));
-             goto lossage;
+             goto error;
            }
          seen |= 1;
              
          lex_match ('=');
          if (!parse_variables (mtf.dict, &mtf.by, &mtf.by_cnt,
                                PV_NO_DUPLICATE | PV_NO_SCRATCH))
            }
          seen |= 1;
              
          lex_match ('=');
          if (!parse_variables (mtf.dict, &mtf.by, &mtf.by_cnt,
                                PV_NO_DUPLICATE | PV_NO_SCRATCH))
-           goto lossage;
+           goto error;
        }
       else if (token != T_ID)
        {
          lex_error (NULL);
        }
       else if (token != T_ID)
        {
          lex_error (NULL);
-         goto lossage;
+         goto error;
        }
       else if (lex_id_match ("FILE", tokid) || lex_id_match ("TABLE", tokid))
        {
          struct mtf_file *file = xmalloc (sizeof *file);
 
        }
       else if (lex_id_match ("FILE", tokid) || lex_id_match ("TABLE", tokid))
        {
          struct mtf_file *file = xmalloc (sizeof *file);
 
-         file->in[0] = file->first[0] = file->last[0] = '\0';
-         file->dict = NULL;
-         file->by = NULL;
-          case_nullify (&file->input);
-
          if (lex_match_id ("FILE"))
            file->type = MTF_FILE;
          else if (lex_match_id ("TABLE"))
          if (lex_match_id ("FILE"))
            file->type = MTF_FILE;
          else if (lex_match_id ("TABLE"))
@@ -595,6 +700,15 @@ cmd_match_files (void)
          else
            assert (0);
 
          else
            assert (0);
 
+         file->by = NULL;
+          file->handle = NULL;
+          file->reader = NULL;
+         file->dict = NULL;
+         file->in[0] = '\0';
+          file->first[0] = '\0';
+          file->last[0] = '\0';
+          case_nullify (&file->input);
+
          /* FILEs go first, then TABLEs. */
          if (file->type == MTF_TABLE || first_table == NULL)
            {
          /* FILEs go first, then TABLEs. */
          if (file->type == MTF_TABLE || first_table == NULL)
            {
@@ -624,13 +738,14 @@ cmd_match_files (void)
          
          if (lex_match ('*'))
            {
          
          if (lex_match ('*'))
            {
-             file->handle = NULL;
-
+              file->handle = NULL;
+             file->reader = NULL;
+              
              if (seen & 2)
                {
                  msg (SE, _("The active file may not be specified more "
                             "than once."));
              if (seen & 2)
                {
                  msg (SE, _("The active file may not be specified more "
                             "than once."));
-                 goto lossage;
+                 goto error;
                }
              seen |= 2;
 
                }
              seen |= 2;
 
@@ -639,7 +754,7 @@ cmd_match_files (void)
                {
                  msg (SE, _("Cannot specify the active file since no active "
                             "file has been defined."));
                {
                  msg (SE, _("Cannot specify the active file since no active "
                             "file has been defined."));
-                 goto lossage;
+                 goto error;
                }
 
               if (temporary != 0)
                }
 
               if (temporary != 0)
@@ -650,25 +765,21 @@ cmd_match_files (void)
                          "Temporary transformations will be made permanent."));
                   cancel_temporary (); 
                 }
                          "Temporary transformations will be made permanent."));
                   cancel_temporary (); 
                 }
+
+              file->dict = default_dict;
            }
          else
            {
            }
          else
            {
-             file->handle = fh_parse_file_handle ();
-             if (!file->handle)
-               goto lossage;
-           }
+              file->handle = fh_parse ();
+             if (file->handle == NULL)
+               goto error;
+
+              file->reader = sfm_open_reader (file->handle, &file->dict, NULL);
+              if (file->reader == NULL)
+                goto error;
 
 
-         if (file->handle)
-           {
-             file->dict = sfm_read_dictionary (file->handle, NULL);
-             if (!file->dict)
-               goto lossage;
               case_create (&file->input, dict_get_next_value_idx (file->dict));
            }
               case_create (&file->input, dict_get_next_value_idx (file->dict));
            }
-         else
-           file->dict = default_dict;
-         if (!mtf_merge_dictionary (mtf.dict, file))
-           goto lossage;
        }
       else if (lex_id_match ("IN", tokid)
               || lex_id_match ("FIRST", tokid)
        }
       else if (lex_id_match ("IN", tokid)
               || lex_id_match ("FIRST", tokid)
@@ -681,7 +792,7 @@ cmd_match_files (void)
            {
              msg (SE, _("IN, FIRST, and LAST subcommands may not occur "
                         "before the first FILE or TABLE."));
            {
              msg (SE, _("IN, FIRST, and LAST subcommands may not occur "
                         "before the first FILE or TABLE."));
-             goto lossage;
+             goto error;
            }
 
          if (lex_match_id ("IN"))
            }
 
          if (lex_match_id ("IN"))
@@ -709,7 +820,7 @@ cmd_match_files (void)
          if (token != T_ID)
            {
              lex_error (NULL);
          if (token != T_ID)
            {
              lex_error (NULL);
-             goto lossage;
+             goto error;
            }
 
          if (*name)
            }
 
          if (*name)
@@ -717,7 +828,7 @@ cmd_match_files (void)
              msg (SE, _("Multiple %s subcommands for a single FILE or "
                         "TABLE."),
                   sbc);
              msg (SE, _("Multiple %s subcommands for a single FILE or "
                         "TABLE."),
                   sbc);
-             goto lossage;
+             goto error;
            }
          strcpy (name, tokid);
          lex_get ();
            }
          strcpy (name, tokid);
          lex_get ();
@@ -727,24 +838,22 @@ cmd_match_files (void)
              msg (SE, _("Duplicate variable name %s while creating %s "
                         "variable."),
                   name, sbc);
              msg (SE, _("Duplicate variable name %s while creating %s "
                         "variable."),
                   name, sbc);
-             goto lossage;
+             goto error;
            }
        }
       else if (lex_id_match ("RENAME", tokid)
               || lex_id_match ("KEEP", tokid)
               || lex_id_match ("DROP", tokid))
        {
            }
        }
       else if (lex_id_match ("RENAME", tokid)
               || lex_id_match ("KEEP", tokid)
               || lex_id_match ("DROP", tokid))
        {
-         int options = GTSV_OPT_MATCH_FILES;
-         
          if (mtf.tail == NULL)
            {
              msg (SE, _("RENAME, KEEP, and DROP subcommands may not occur "
                         "before the first FILE or TABLE."));
          if (mtf.tail == NULL)
            {
              msg (SE, _("RENAME, KEEP, and DROP subcommands may not occur "
                         "before the first FILE or TABLE."));
-             goto lossage;
+             goto error;
            }
 
            }
 
-         if (!trim_dictionary (mtf.tail->dict, &options))
-           goto lossage;
+         if (!trim_dictionary (mtf.tail->dict, OP_MATCH, NULL))
+           goto error;
        }
       else if (lex_match_id ("MAP"))
        {
        }
       else if (lex_match_id ("MAP"))
        {
@@ -753,26 +862,27 @@ cmd_match_files (void)
       else
        {
          lex_error (NULL);
       else
        {
          lex_error (NULL);
-         goto lossage;
+         goto error;
        }
     }
   while (token != '.');
 
        }
     }
   while (token != '.');
 
+  for (iter = mtf.head; iter != NULL; iter = iter->next) 
+    mtf_merge_dictionary (mtf.dict, iter);
+
   if (seen & 4)
     {
       if (!(seen & 1))
        {
          msg (SE, _("The BY subcommand is required when a TABLE subcommand "
                     "is given."));
   if (seen & 4)
     {
       if (!(seen & 1))
        {
          msg (SE, _("The BY subcommand is required when a TABLE subcommand "
                     "is given."));
-         goto lossage;
+         goto error;
        }
     }
 
   if (seen & 1)
     {
        }
     }
 
   if (seen & 1)
     {
-      struct mtf_file *iter;
-
-      for (iter = mtf.head; iter; iter = iter->next)
+      for (iter = mtf.head; iter != NULL; iter = iter->next)
        {
          int i;
          
        {
          int i;
          
@@ -786,7 +896,7 @@ cmd_match_files (void)
                  msg (SE, _("File %s lacks BY variable %s."),
                       iter->handle ? handle_get_name (iter->handle) : "*",
                       mtf.by[i]->name);
                  msg (SE, _("File %s lacks BY variable %s."),
                       iter->handle ? handle_get_name (iter->handle) : "*",
                       mtf.by[i]->name);
-                 goto lossage;
+                 goto error;
                }
            }
        }
                }
            }
        }
@@ -852,7 +962,7 @@ cmd_match_files (void)
   mtf_free (&mtf);
   return CMD_SUCCESS;
   
   mtf_free (&mtf);
   return CMD_SUCCESS;
   
-lossage:
+error:
   mtf_free (&mtf);
   return CMD_FAILURE;
 }
   mtf_free (&mtf);
   return CMD_FAILURE;
 }
@@ -903,12 +1013,11 @@ var_type_description (struct variable *v)
 static void
 mtf_free_file (struct mtf_file *file)
 {
 static void
 mtf_free_file (struct mtf_file *file)
 {
-  fh_close_handle (file->handle);
-  if (file->dict != NULL && file->dict != default_dict)
-    dict_destroy (file->dict);
   free (file->by);
   free (file->by);
-  if (file->handle)
-    case_destroy (&file->input);
+  sfm_close_reader (file->reader);
+  if (file->dict != default_dict)
+    dict_destroy (file->dict);
+  case_destroy (&file->input);
   free (file);
 }
 
   free (file);
 }
 
@@ -954,7 +1063,7 @@ mtf_delete_file_in_place (struct mtf_proc *mtf, struct mtf_file **file)
     for (i = 0; i < dict_get_var_cnt (f->dict); i++)
       {
        struct variable *v = dict_get_var (f->dict, i);
     for (i = 0; i < dict_get_var_cnt (f->dict); i++)
       {
        struct variable *v = dict_get_var (f->dict, i);
-        union value *out = case_data_rw (mtf->mtf_case, v->p.mtf.master->fv);
+        union value *out = case_data_rw (mtf->mtf_case, get_master (v)->fv);
          
        if (v->type == NUMERIC)
           out->f = SYSMIS;
          
        if (v->type == NUMERIC)
           out->f = SYSMIS;
@@ -977,7 +1086,7 @@ mtf_read_nonactive_records (void *mtf_ UNUSED)
     {
       if (iter->handle)
        {
     {
       if (iter->handle)
        {
-         if (!sfm_read_case (iter->handle, &iter->input, iter->dict))
+         if (!sfm_read_case (iter->reader, &iter->input))
            mtf_delete_file_in_place (mtf, &iter);
          else
            iter = iter->next;
            mtf_delete_file_in_place (mtf, &iter);
          else
            iter = iter->next;
@@ -1121,7 +1230,7 @@ mtf_processing (struct ccase *c, void *mtf_ UNUSED)
            case 1:
              if (iter->handle == NULL)
                return 1;
            case 1:
              if (iter->handle == NULL)
                return 1;
-             if (sfm_read_case (iter->handle, &iter->input, iter->dict))
+             if (sfm_read_case (iter->reader, &iter->input))
                goto again;
              mtf_delete_file_in_place (mtf, &iter);
              break;
                goto again;
              mtf_delete_file_in_place (mtf, &iter);
              break;
@@ -1149,14 +1258,14 @@ mtf_processing (struct ccase *c, void *mtf_ UNUSED)
               struct ccase *record;
               union value *out;
          
               struct ccase *record;
               union value *out;
          
-             if (mtf->seq_nums[v->p.mtf.master->index] == mtf->seq_num)
+             if (mtf->seq_nums[get_master (v)->index] == mtf->seq_num)
                continue;
                continue;
-              mtf->seq_nums[v->p.mtf.master->index] = mtf->seq_num;
+              mtf->seq_nums[get_master (v)->index] = mtf->seq_num;
 
               record = case_is_null (&iter->input) ? c : &iter->input;
 
               assert (v->type == NUMERIC || v->type == ALPHA);
 
               record = case_is_null (&iter->input) ? c : &iter->input;
 
               assert (v->type == NUMERIC || v->type == ALPHA);
-              out = case_data_rw (mtf->mtf_case, v->p.mtf.master->fv);
+              out = case_data_rw (mtf->mtf_case, get_master (v)->fv);
              if (v->type == NUMERIC)
                out->f = case_num (record, v->fv);
              else
              if (v->type == NUMERIC)
                out->f = case_num (record, v->fv);
              else
@@ -1176,11 +1285,11 @@ mtf_processing (struct ccase *c, void *mtf_ UNUSED)
              struct variable *v = dict_get_var (iter->dict, i);
               union value *out;
          
              struct variable *v = dict_get_var (iter->dict, i);
               union value *out;
          
-             if (mtf->seq_nums[v->p.mtf.master->index] == mtf->seq_num)
+             if (mtf->seq_nums[get_master (v)->index] == mtf->seq_num)
                continue;
                continue;
-              mtf->seq_nums[v->p.mtf.master->index] = mtf->seq_num;
+              mtf->seq_nums[get_master (v)->index] = mtf->seq_num;
 
 
-              out = case_data_rw (mtf->mtf_case, v->p.mtf.master->fv);
+              out = case_data_rw (mtf->mtf_case, get_master (v)->fv);
              if (v->type == NUMERIC)
                 out->f = SYSMIS;
              else
              if (v->type == NUMERIC)
                 out->f = SYSMIS;
              else
@@ -1202,9 +1311,9 @@ mtf_processing (struct ccase *c, void *mtf_ UNUSED)
        {
          struct mtf_file *next = iter->next_min;
          
        {
          struct mtf_file *next = iter->next_min;
          
-         if (iter->handle)
+         if (iter->reader != NULL)
            {
            {
-             if (!sfm_read_case (iter->handle, &iter->input, iter->dict))
+             if (!sfm_read_case (iter->reader, &iter->input))
                mtf_delete_file_in_place (mtf, &iter);
            }
 
                mtf_delete_file_in_place (mtf, &iter);
            }
 
@@ -1218,8 +1327,7 @@ mtf_processing (struct ccase *c, void *mtf_ UNUSED)
   return (mtf->head && mtf->head->type != MTF_TABLE);
 }
 
   return (mtf->head && mtf->head->type != MTF_TABLE);
 }
 
-/* Merge the dictionary for file F into the master dictionary
-   mtf_dict. */
+/* Merge the dictionary for file F into master dictionary M. */
 static int
 mtf_merge_dictionary (struct dictionary *const m, struct mtf_file *f)
 {
 static int
 mtf_merge_dictionary (struct dictionary *const m, struct mtf_file *f)
 {
@@ -1286,25 +1394,55 @@ mtf_merge_dictionary (struct dictionary *const m, struct mtf_file *f)
                 var_type_description (dv), var_type_description (mv));
            return 0;
          }
                 var_type_description (dv), var_type_description (mv));
            return 0;
          }
-       dv->p.mtf.master = mv;
+        set_master (dv, mv);
       }
   }
 
   return 1;
 }
       }
   }
 
   return 1;
 }
+
+/* Marks V's master variable as MASTER. */
+static void
+set_master (struct variable *v, struct variable *master) 
+{
+  var_attach_aux (v, master, NULL);
+}
+
+/* Returns the master variable corresponding to V,
+   as set with set_master(). */
+static struct variable *
+get_master (struct variable *v) 
+{
+  assert (v->aux != NULL);
+  return v->aux;
+}
 \f
 /* IMPORT command. */
 
 \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)
 {
 /* Parses the IMPORT command. */
 int
 cmd_import (void)
 {
-  struct file_handle *handle = NULL;
-  struct dictionary *dict;
-  struct get_pgm *pgm;
-  int options = GTSV_OPT_NONE;
+  struct import_pgm *pgm = NULL;
+  struct file_handle *fh = NULL;
+  struct dictionary *dict = NULL;
   int type;
 
   int type;
 
+  pgm = xmalloc (sizeof *pgm);
+  pgm->reader = NULL;
+  pgm->map = NULL;
+  case_nullify (&pgm->bounce);
+
   for (;;)
     {
       lex_match ('/');
   for (;;)
     {
       lex_match ('/');
@@ -1313,8 +1451,8 @@ cmd_import (void)
        {
          lex_match ('=');
 
        {
          lex_match ('=');
 
-         handle = fh_parse_file_handle ();
-         if (handle == NULL)
+         fh = fh_parse ();
+         if (fh == NULL)
            return CMD_FAILURE;
        }
       else if (lex_match_id ("TYPE"))
            return CMD_FAILURE;
        }
       else if (lex_match_id ("TYPE"))
@@ -1341,41 +1479,76 @@ cmd_import (void)
 
   discard_variables ();
 
 
   discard_variables ();
 
-  dict = pfm_read_dictionary (handle, NULL);
-  if (dict == NULL)
+  pgm->reader = pfm_open_reader (fh, &dict, NULL);
+  if (pgm->reader == NULL)
     return CMD_FAILURE;
     return CMD_FAILURE;
-
-  if (0 == trim_dictionary (dict, &options))
-    {
-      fh_close_handle (handle);
-      return CMD_FAILURE;
-    }
-
-  dict_compact_values (dict);
-
+  case_create (&pgm->bounce, dict_get_next_value_idx (dict));
+  
+  start_case_map (dict);
+  if (!trim_dictionary (dict, OP_READ, NULL))
+    goto error;
+  pgm->map = finish_case_map (dict);
+  
   dict_destroy (default_dict);
   default_dict = dict;
 
   dict_destroy (default_dict);
   default_dict = dict;
 
-  pgm = xmalloc (sizeof *pgm);
-  pgm->handle = handle;
-  pgm->case_size = dict_get_case_size (default_dict);
   vfm_source = create_case_source (&import_source_class, default_dict, pgm);
 
   return CMD_SUCCESS;
   vfm_source = create_case_source (&import_source_class, default_dict, pgm);
 
   return CMD_SUCCESS;
+
+ error:
+  import_pgm_free (pgm);
+  if (dict != NULL)
+    dict_destroy (dict);
+  return CMD_FAILURE;
 }
 
 }
 
-/* Reads all the cases from the data file and passes them to
-   write_case(). */
+/* 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,
 static void
 import_source_read (struct case_source *source,
-                    struct ccase *c,
-                    write_case_func *write_case, write_case_data wc_data)
+                 struct ccase *c,
+                 write_case_func *write_case, write_case_data wc_data)
 {
 {
-  struct get_pgm *pgm = source->aux;
-  
-  while (pfm_read_case (pgm->handle, c, default_dict))
-    if (!write_case (wc_data))
-      break;
+  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 =
 }
 
 const struct case_source_class import_source_class =
@@ -1383,85 +1556,128 @@ const struct case_source_class import_source_class =
     "IMPORT",
     NULL,
     import_source_read,
     "IMPORT",
     NULL,
     import_source_read,
-    get_source_destroy,
+    import_source_destroy,
   };
   };
+
 \f
 \f
-static int export_write_case_func (struct ccase *c, void *);
-     
-/* Parses the EXPORT command.  */
-/* FIXME: same as cmd_save_internal(). */
-int
-cmd_export (void)
-{
-  struct file_handle *handle;
-  struct dictionary *dict;
-  int options = GTSV_OPT_SAVE;
+/* Case map.
 
 
-  struct save_trns *t;
+   A case map copies data from a case that corresponds for one
+   dictionary to a case that corresponds to a second dictionary
+   derived from the first by, optionally, deleting, reordering,
+   or renaming variables.  (No new variables may be created.)
+   */
 
 
-  int i;
+/* A case map. */
+struct case_map
+  {
+    size_t value_cnt;   /* Number of values in map. */
+    int *map;           /* For each destination index, the
+                           corresponding source index. */
+  };
 
 
-  lex_match ('/');
-  if (lex_match_id ("OUTFILE"))
-    lex_match ('=');
+/* Prepares dictionary D for producing a case map.  Afterward,
+   the caller may delete, reorder, or rename variables within D
+   at will before using finish_case_map() to produce the case
+   map.
 
 
-  handle = fh_parse_file_handle ();
-  if (handle == NULL)
-    return CMD_FAILURE;
-
-  dict = dict_clone (default_dict);
-  for (i = 0; i < dict_get_var_cnt (dict); i++)
-    dict_get_var (dict, i)->aux = dict_get_var (default_dict, i);
-  if (0 == trim_dictionary (dict, &options))
+   Uses D's aux members, which may not otherwise be in use. */
+static void
+start_case_map (struct dictionary *d) 
+{
+  size_t var_cnt = dict_get_var_cnt (d);
+  size_t i;
+  
+  for (i = 0; i < var_cnt; i++)
     {
     {
-      fh_close_handle (handle);
-      return CMD_FAILURE;
+      struct variable *v = dict_get_var (d, i);
+      int *src_fv = xmalloc (sizeof *src_fv);
+      *src_fv = v->fv;
+      var_attach_aux (v, src_fv, var_dtor_free);
     }
     }
+}
+
+/* Produces a case map from dictionary D, which must have been
+   previously prepared with start_case_map().
 
 
-  /* Write dictionary. */
-  if (!pfm_write_dictionary (handle, dict))
+   Does not retain any reference to D, and clears the aux members
+   set up by start_case_map().
+
+   Returns the new case map, or a null pointer if no mapping is
+   required (that is, no data has changed position). */
+static struct case_map *
+finish_case_map (struct dictionary *d) 
+{
+  struct case_map *map;
+  size_t var_cnt = dict_get_var_cnt (d);
+  size_t i;
+  int identity_map;
+
+  map = xmalloc (sizeof *map);
+  map->value_cnt = dict_get_next_value_idx (d);
+  map->map = xmalloc (sizeof *map->map * map->value_cnt);
+  for (i = 0; i < map->value_cnt; i++)
+    map->map[i] = -1;
+
+  identity_map = 1;
+  for (i = 0; i < var_cnt; i++) 
     {
     {
-      dict_destroy (dict);
-      fh_close_handle (handle);
-      return CMD_FAILURE;
+      struct variable *v = dict_get_var (d, i);
+      int src_fv = *(int *) var_detach_aux (v);
+      size_t idx;
+
+      if (v->fv != src_fv)
+        identity_map = 0;
+      
+      for (idx = 0; idx < v->nv; idx++)
+        {
+          int src_idx = src_fv + idx;
+          int dst_idx = v->fv + idx;
+          
+          assert (map->map[dst_idx] == -1);
+          map->map[dst_idx] = src_idx;
+        }
     }
 
     }
 
-  /* Fill in transformation structure. */
-  t = xmalloc (sizeof *t);
-  t->h.proc = save_trns_proc;
-  t->h.free = save_trns_free;
-  t->f = handle;
-  t->nvar = dict_get_var_cnt (dict);
-  t->var = xmalloc (sizeof *t->var * t->nvar);
-  for (i = 0; i < t->nvar; i++)
-    t->var[i] = dict_get_var (dict, i)->aux;
-  t->case_buf = xmalloc (sizeof *t->case_buf * t->nvar);
-  dict_destroy (dict);
+  if (identity_map) 
+    {
+      destroy_case_map (map);
+      return NULL;
+    }
 
 
-  procedure (export_write_case_func, t);
-  save_trns_free (&t->h);
+  while (map->value_cnt > 0 && map->map[map->value_cnt - 1] == -1)
+    map->value_cnt--;
 
 
-  return CMD_SUCCESS;
+  return map;
 }
 
 }
 
-/* Writes case C to the EXPORT file. */
-static int
-export_write_case_func (struct ccase *c, void *aux)
+/* Maps from SRC to DST, applying case map MAP. */
+static void
+map_case (const struct case_map *map,
+          const struct ccase *src, struct ccase *dst) 
 {
 {
-  struct save_trns *t = aux;
-  union value *p = (union value *) t->case_buf;
-  int i;
+  size_t dst_idx;
 
 
-  for (i = 0; i < t->nvar; i++)
-    {
-      struct variable *v = t->var[i];
+  assert (map != NULL);
+  assert (src != NULL);
+  assert (dst != NULL);
+  assert (src != dst);
 
 
-      if (v->type == NUMERIC)
-       (*p++).f = case_num (c, v->fv);
-      else
-       (*p++).c = (char *) case_str (c, v->fv);
+  for (dst_idx = 0; dst_idx < map->value_cnt; dst_idx++)
+    {
+      int src_idx = map->map[dst_idx];
+      if (src_idx != -1)
+        *case_data_rw (dst, dst_idx) = *case_data (src, src_idx);
     }
     }
+}
 
 
-  pfm_write_case (t->f, (union value *) t->case_buf);
-  return 1;
+/* Destroys case map MAP. */
+static void
+destroy_case_map (struct case_map *map) 
+{
+  if (map != NULL) 
+    {
+      free (map->map);
+      free (map);
+    }
 }
 }
index 83dd2b66ef49ac75ed1b8592469022dfe3958c37..9b4f93c4e62fa5adac77728994a391dcef7018a5 100644 (file)
@@ -70,6 +70,7 @@ extern void stifle_history ();
 
 #include "alloc.h"
 #include "command.h"
 
 #include "alloc.h"
 #include "command.h"
+#include "dictionary.h"
 #include "do-ifP.h"
 #include "error.h"
 #include "expr.h"
 #include "do-ifP.h"
 #include "error.h"
 #include "expr.h"
@@ -176,9 +177,6 @@ init_glob (int argc UNUSED, char **argv)
   logfn = xstrdup ("pspp.log");
   logfile = NULL;
 
   logfn = xstrdup ("pspp.log");
   logfile = NULL;
 
-  /* file-handle.h */
-  fh_init_files ();
-  
   get_date ();
 }
 
   get_date ();
 }
 
index e394745e96496d6d9fae5f8f5d8c1ee01d6f21a1..06e303e559cd3cf16083efd78659d02c4733cb60 100644 (file)
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
    02111-1307, USA. */
 
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
    02111-1307, USA. */
 
+#include <config.h>
+#include <stdlib.h>
+#include "alloc.h"
 #include "hash.h"
 #include "group.h"
 #include "hash.h"
 #include "group.h"
-#include <string.h>
+#include "group_proc.h"
+#include "str.h"
+#include "var.h"
 
 
 /* Return -1 if the id of a is less than b; +1 if greater than and 
 
 
 /* Return -1 if the id of a is less than b; +1 if greater than and 
@@ -48,7 +53,17 @@ hash_group(const struct group_statistics *g, int width)
 
 
 void  
 
 
 void  
-free_group(struct group_statistics *v, void *aux)
+free_group(struct group_statistics *v, void *aux UNUSED)
 {
   free(v);
 }
 {
   free(v);
 }
+
+
+struct group_proc *
+group_proc_get (struct variable *v)
+{
+  /* This is not ideal, obviously. */
+  if (v->aux == NULL) 
+    var_attach_aux (v, xmalloc (sizeof (struct group_proc)), var_dtor_free);
+  return v->aux;
+}
index af1b97580748147a95fbd81e1e968906febabe8b..36c15e6552aa03c3b2a22397af90d41d79bfc062 100644 (file)
@@ -45,4 +45,7 @@ struct group_proc
 
 };
 
 
 };
 
+struct variable;
+struct group_proc *group_proc_get (struct variable *);
+
 #endif
 #endif
index 27e7b4b13cff7b736a1f5044e196b2c2f2604ca3..5429a90321f863e323eae1666ddccec060849df0 100644 (file)
@@ -78,6 +78,7 @@ write_legend(struct chart *ch, struct normal_curve *norm)
 void
 draw_histogram(struct chart *ch, 
               const struct variable *var,
 void
 draw_histogram(struct chart *ch, 
               const struct variable *var,
+               const struct freq_tab *frq_tab,
               const char *title, 
               struct normal_curve *norm,
               int show_normal)
               const char *title, 
               struct normal_curve *norm,
               int show_normal)
@@ -96,8 +97,6 @@ draw_histogram(struct chart *ch,
 
   double ordinate_values[BINS];
 
 
   double ordinate_values[BINS];
 
-  const struct freq_tab *frq_tab = &var->p.frq.tab ;
-
   struct hsh_iterator hi;
   struct hsh_table *fh = frq_tab->data;
   struct freq *frq;
   struct hsh_iterator hi;
   struct hsh_table *fh = frq_tab->data;
   struct freq *frq;
index 0cbf66f325569d54b828c98eac639c2124ac029b..e27b9dee83bb02bc94b8c96c0eb201793369512e 100644 (file)
@@ -25,7 +25,8 @@
 #include "case.h"
 #include "command.h"
 #include "data-list.h"
 #include "case.h"
 #include "command.h"
 #include "data-list.h"
-#include "dfm.h"
+#include "dfm-read.h"
+#include "dictionary.h"
 #include "error.h"
 #include "expr.h"
 #include "file-handle.h"
 #include "error.h"
 #include "expr.h"
 #include "file-handle.h"
@@ -312,7 +313,7 @@ struct reread_trns
   {
     struct trns_header h;
 
   {
     struct trns_header h;
 
-    struct file_handle *handle;        /* File to move file pointer back on. */
+    struct dfm_reader *reader; /* File to move file pointer back on. */
     struct expression *column; /* Column to reset file pointer to. */
   };
 
     struct expression *column; /* Column to reset file pointer to. */
   };
 
@@ -320,16 +321,11 @@ struct reread_trns
 int
 cmd_reread (void)
 {
 int
 cmd_reread (void)
 {
-  /* File to be re-read. */
-  struct file_handle *h;
-  
-  /* Expression for column to set file pointer to. */
-  struct expression *e;
-
-  /* Created transformation. */
-  struct reread_trns *t;
+  struct file_handle *fh;       /* File to be re-read. */
+  struct expression *e;         /* Expression for column to set. */
+  struct reread_trns *t;        /* Created transformation. */
 
 
-  h = default_handle;
+  fh = default_handle;
   e = NULL;
   while (token != '.')
     {
   e = NULL;
   while (token != '.')
     {
@@ -351,8 +347,8 @@ cmd_reread (void)
       else if (lex_match_id ("FILE"))
        {
          lex_match ('=');
       else if (lex_match_id ("FILE"))
        {
          lex_match ('=');
-          h = fh_parse_file_handle ();
-         if (h == NULL)
+          fh = fh_parse ();
+         if (fh == NULL)
            {
              expr_free (e);
              return CMD_FAILURE;
            {
              expr_free (e);
              return CMD_FAILURE;
@@ -369,7 +365,7 @@ cmd_reread (void)
   t = xmalloc (sizeof *t);
   t->h.proc = reread_trns_proc;
   t->h.free = reread_trns_free;
   t = xmalloc (sizeof *t);
   t->h.proc = reread_trns_proc;
   t->h.free = reread_trns_free;
-  t->handle = h;
+  t->reader = dfm_open_reader (fh);
   t->column = e;
   add_transformation ((struct trns_header *) t);
 
   t->column = e;
   add_transformation ((struct trns_header *) t);
 
@@ -384,7 +380,7 @@ reread_trns_proc (struct trns_header * pt, struct ccase * c,
   struct reread_trns *t = (struct reread_trns *) pt;
 
   if (t->column == NULL)
   struct reread_trns *t = (struct reread_trns *) pt;
 
   if (t->column == NULL)
-    dfm_reread_record (t->handle, 1);
+    dfm_reread_record (t->reader, 1);
   else
     {
       union value column;
   else
     {
       union value column;
@@ -394,19 +390,21 @@ reread_trns_proc (struct trns_header * pt, struct ccase * c,
        {
          msg (SE, _("REREAD: Column numbers must be positive finite "
               "numbers.  Column set to 1."));
        {
          msg (SE, _("REREAD: Column numbers must be positive finite "
               "numbers.  Column set to 1."));
-         dfm_reread_record (t->handle, 1);
+         dfm_reread_record (t->reader, 1);
        }
       else
        }
       else
-       dfm_reread_record (t->handle, column.f);
+       dfm_reread_record (t->reader, column.f);
     }
   return -1;
 }
 
 /* Frees a REREAD transformation. */
 static void
     }
   return -1;
 }
 
 /* Frees a REREAD transformation. */
 static void
-reread_trns_free (struct trns_header * t)
+reread_trns_free (struct trns_header *t_)
 {
 {
-  expr_free (((struct reread_trns *) t)->column);
+  struct reread_trns *t = (struct reread_trns *) t_;
+  expr_free (t->column);
+  dfm_close_reader (t->reader);
 }
 
 /* Parses END FILE command. */
 }
 
 /* Parses END FILE command. */
index 0e36b87a8c12b59817c944be0e14bf14430cb2f6..11e7480acfc115f436d6a81e6d7c336a933f5352 100644 (file)
@@ -24,6 +24,8 @@
 #include "error.h"
 #include "case.h"
 #include "casefile.h"
 #include "error.h"
 #include "case.h"
 #include "casefile.h"
+#include "dictionary.h"
+#include "group_proc.h"
 #include "hash.h"
 #include "str.h"
 #include "var.h"
 #include "hash.h"
 #include "str.h"
 #include "var.h"
@@ -162,17 +164,18 @@ levene_precalc (const struct levene_info *l)
   for(i = 0; i < l->n_dep ; ++i ) 
     {
       struct variable *var = l->v_dep[i];
   for(i = 0; i < l->n_dep ; ++i ) 
     {
       struct variable *var = l->v_dep[i];
+      struct group_proc *gp = group_proc_get (var);
       struct group_statistics *gs;
       struct hsh_iterator hi;
 
       lz[i].grand_total = 0;
       lz[i].total_n = 0;
       struct group_statistics *gs;
       struct hsh_iterator hi;
 
       lz[i].grand_total = 0;
       lz[i].total_n = 0;
-      lz[i].n_groups = var->p.grp_data.n_groups ; 
+      lz[i].n_groups = gp->n_groups ; 
 
       
 
       
-      for ( gs = hsh_first(var->p.grp_data.group_hash, &hi);
+      for ( gs = hsh_first(gp->group_hash, &hi);
            gs != 0;
            gs != 0;
-           gs = hsh_next(var->p.grp_data.group_hash, &hi))
+           gs = hsh_next(gp->group_hash, &hi))
        {
          gs->lz_total = 0;
        }
        {
          gs->lz_total = 0;
        }
@@ -212,11 +215,12 @@ levene_calc (const struct ccase *c, void *_l)
   for (i = 0; i < l->n_dep; ++i) 
     {
       struct variable *var = l->v_dep[i];
   for (i = 0; i < l->n_dep; ++i) 
     {
       struct variable *var = l->v_dep[i];
+      struct group_proc *gp = group_proc_get (var);
       double levene_z;
       const union value *v = case_data (c, var->fv);
       struct group_statistics *gs;
 
       double levene_z;
       const union value *v = case_data (c, var->fv);
       struct group_statistics *gs;
 
-      gs = hsh_find(var->p.grp_data.group_hash,(void *) &key );
+      gs = hsh_find(gp->group_hash,(void *) &key );
 
       if ( 0 == gs ) 
        continue ;
 
       if ( 0 == gs ) 
        continue ;
@@ -271,7 +275,7 @@ levene2_precalc (void *_l)
       struct group_statistics *g;
 
       struct variable *var = l->v_dep[v] ;
       struct group_statistics *g;
 
       struct variable *var = l->v_dep[v] ;
-      struct hsh_table *hash = var->p.grp_data.group_hash;
+      struct hsh_table *hash = group_proc_get (var)->group_hash;
 
 
       for(g = (struct group_statistics *) hsh_first(hash,&hi);
 
 
       for(g = (struct group_statistics *) hsh_first(hash,&hi);
@@ -321,7 +325,7 @@ levene2_calc (const struct ccase *c, void *_l)
       const union value *v = case_data (c, var->fv);
       struct group_statistics *gs;
 
       const union value *v = case_data (c, var->fv);
       struct group_statistics *gs;
 
-      gs = hsh_find(var->p.grp_data.group_hash,(void *) &key );
+      gs = hsh_find(group_proc_get (var)->group_hash,(void *) &key );
 
       if ( 0 == gs ) 
        continue;
 
       if ( 0 == gs ) 
        continue;
@@ -351,7 +355,8 @@ levene2_postcalc (void *_l)
       struct group_statistics *g;
 
       struct variable *var = l->v_dep[v] ;
       struct group_statistics *g;
 
       struct variable *var = l->v_dep[v] ;
-      struct hsh_table *hash = var->p.grp_data.group_hash;
+      struct group_proc *gp = group_proc_get (var);
+      struct hsh_table *hash = gp->group_hash;
 
       for(g = (struct group_statistics *) hsh_first(hash,&hi);
          g != 0 ;
 
       for(g = (struct group_statistics *) hsh_first(hash,&hi);
          g != 0 ;
@@ -359,12 +364,11 @@ levene2_postcalc (void *_l)
        {
          lz_numerator += g->n * pow2(g->lz_mean - lz[v].grand_mean );
        }
        {
          lz_numerator += g->n * pow2(g->lz_mean - lz[v].grand_mean );
        }
-      lz_numerator *= ( l->v_dep[v]->p.grp_data.ugs.n - 
-                       l->v_dep[v]->p.grp_data.n_groups );
+      lz_numerator *= ( gp->ugs.n - gp->n_groups );
 
 
-      lz_denominator[v] *= (l->v_dep[v]->p.grp_data.n_groups - 1);
+      lz_denominator[v] *= (gp->n_groups - 1);
 
 
-      l->v_dep[v]->p.grp_data.levene = lz_numerator / lz_denominator[v] ;
+      gp->levene = lz_numerator / lz_denominator[v] ;
 
     }
 
 
     }
 
index 0ab9188955cdc91bbf9c94b811bfd008ffcd5ebd..e3ac2ed52e853e27cff9877afc37e6e7659dafda 100644 (file)
@@ -25,6 +25,7 @@
 #include "case.h"
 #include "command.h"
 #include "devind.h"
 #include "case.h"
 #include "command.h"
 #include "devind.h"
+#include "dictionary.h"
 #include "lexer.h"
 #include "error.h"
 #include "magic.h"
 #include "lexer.h"
 #include "error.h"
 #include "magic.h"
@@ -36,6 +37,7 @@
 #include "var.h"
 #include "vfm.h"
 #include "format.h"
 #include "var.h"
 #include "vfm.h"
 #include "format.h"
+/* (headers) */
 
 #include "debug-print.h"
 
 
 #include "debug-print.h"
 
index 5e3f16991393149b27e9f136a921c8cd0db1763f..ebe5ed2257a1f9eaff83b804b8a5b9933296281e 100644 (file)
@@ -22,6 +22,7 @@
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
+#include "dictionary.h"
 #include "do-ifP.h"
 #include "error.h"
 #include "expr.h"
 #include "do-ifP.h"
 #include "error.h"
 #include "expr.h"
index 4c216030726516b2d51a538b6a6e4f0f0266db67..b4aa243518f7851e3adbf496ebe51059ef730a74 100644 (file)
 #include "main.h"
 #include "cmdline.h"
 #include "command.h"
 #include "main.h"
 #include "cmdline.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "getline.h"
 #include "glob.h"
 #include "lexer.h"
 #include "output.h"
 #include "settings.h"
 #include "error.h"
 #include "getline.h"
 #include "glob.h"
 #include "lexer.h"
 #include "output.h"
 #include "settings.h"
+#include "var.h"
 #include <signal.h>
 
 #include <stdlib.h>
 #include <signal.h>
 
 #include <stdlib.h>
@@ -100,6 +102,7 @@ static int
 execute_command (void)
 {
   int result;
 execute_command (void)
 {
   int result;
+  
   /* Read the command's first token.
      We may hit end of file.
      If so, give the line reader a chance to proceed to the next file.
   /* Read the command's first token.
      We may hit end of file.
      If so, give the line reader a chance to proceed to the next file.
@@ -123,6 +126,9 @@ execute_command (void)
   /* Unset the /ALGORITHM subcommand if it was used */
   unset_cmd_algorithm ();
 
   /* Unset the /ALGORITHM subcommand if it was used */
   unset_cmd_algorithm ();
 
+  /* Clear any auxiliary data from the dictionary. */
+  dict_clear_aux (default_dict);
+
   return result;
 }
 
   return result;
 }
 
index e90aef0955ed5872509a27a4a7726912320346df..a699738ddb04ce8ddbc38d847e371f60fed306bd 100644 (file)
@@ -27,7 +27,8 @@
 #include "case.h"
 #include "command.h"
 #include "data-in.h"
 #include "case.h"
 #include "command.h"
 #include "data-in.h"
-#include "dfm.h"
+#include "dfm-read.h"
+#include "dictionary.h"
 #include "error.h"
 #include "file-handle.h"
 #include "lexer.h"
 #include "error.h"
 #include "file-handle.h"
 #include "lexer.h"
 /* FIXME: /N subcommand not implemented.  It should be pretty simple,
    too. */
 
 /* FIXME: /N subcommand not implemented.  It should be pretty simple,
    too. */
 
+/* Different types of variables for MATRIX DATA procedure.  Order is
+   important: these are used for sort keys. */
+enum
+  {
+    MXD_SPLIT,                 /* SPLIT FILE variables. */
+    MXD_ROWTYPE,               /* ROWTYPE_. */
+    MXD_FACTOR,                        /* Factor variables. */
+    MXD_VARNAME,               /* VARNAME_. */
+    MXD_CONTINUOUS,            /* Continuous variables. */
+
+    MXD_COUNT
+  };
+
 /* Format type enums. */
 enum format_type
   {
 /* Format type enums. */
 enum format_type
   {
@@ -102,7 +116,7 @@ static const char *content_names[PROX + 1] =
 struct matrix_data_pgm 
   {
     struct pool *container;     /* Arena used for all allocations. */
 struct matrix_data_pgm 
   {
     struct pool *container;     /* Arena used for all allocations. */
-    struct file_handle *data_file; /* The data file to be read. */
+    struct dfm_reader *reader;  /* Data file to read. */
 
     /* Format. */
     enum format_type fmt;      /* LIST or FREE. */
 
     /* Format. */
     enum format_type fmt;      /* LIST or FREE. */
@@ -133,21 +147,30 @@ struct matrix_data_pgm
                                    first continuous variable. */
   };
 
                                    first continuous variable. */
   };
 
+/* Auxiliary data attached to MATRIX DATA variables. */
+struct mxd_var 
+  {
+    int var_type;              /* Variable type. */
+    int sub_type;              /* Subtype. */
+  };
+
 static const struct case_source_class matrix_data_with_rowtype_source_class;
 static const struct case_source_class matrix_data_without_rowtype_source_class;
 
 static const struct case_source_class matrix_data_with_rowtype_source_class;
 static const struct case_source_class matrix_data_without_rowtype_source_class;
 
-static int compare_variables_by_mxd_vartype (const void *pa,
+static int compare_variables_by_mxd_var_type (const void *pa,
                                             const void *pb);
 static void read_matrices_without_rowtype (struct matrix_data_pgm *);
 static void read_matrices_with_rowtype (struct matrix_data_pgm *);
 static int string_to_content_type (char *, int *);
                                             const void *pb);
 static void read_matrices_without_rowtype (struct matrix_data_pgm *);
 static void read_matrices_with_rowtype (struct matrix_data_pgm *);
 static int string_to_content_type (char *, int *);
+static void attach_mxd_aux (struct variable *, int var_type, int sub_type);
 
 int
 cmd_matrix_data (void)
 {
   struct pool *pool;
   struct matrix_data_pgm *mx;
 
 int
 cmd_matrix_data (void)
 {
   struct pool *pool;
   struct matrix_data_pgm *mx;
-  
+  struct file_handle *fh = NULL;
+    
   unsigned seen = 0;
   
   discard_variables ();
   unsigned seen = 0;
   
   discard_variables ();
@@ -155,7 +178,7 @@ cmd_matrix_data (void)
   pool = pool_create ();
   mx = pool_alloc (pool, sizeof *mx);
   mx->container = pool;
   pool = pool_create ();
   mx = pool_alloc (pool, sizeof *mx);
   mx->container = pool;
-  mx->data_file = inline_file;
+  mx->reader = NULL;
   mx->fmt = LIST;
   mx->section = LOWER;
   mx->diag = DIAGONAL;
   mx->fmt = LIST;
   mx->section = LOWER;
   mx->diag = DIAGONAL;
@@ -216,9 +239,8 @@ cmd_matrix_data (void)
                if (strcmp (v[i], "ROWTYPE_"))
                  {
                    new_var = dict_create_var_assert (default_dict, v[i], 0);
                if (strcmp (v[i], "ROWTYPE_"))
                  {
                    new_var = dict_create_var_assert (default_dict, v[i], 0);
-                   new_var->p.mxd.vartype = MXD_CONTINUOUS;
-                   new_var->p.mxd.subtype = i;
-                 }
+                    attach_mxd_aux (new_var, MXD_CONTINUOUS, i);
+                  }
                else
                  mx->explicit_rowtype = 1;
                free (v[i]);
                else
                  mx->explicit_rowtype = 1;
                free (v[i]);
@@ -226,18 +248,15 @@ cmd_matrix_data (void)
            free (v);
          }
          
            free (v);
          }
          
-         {
-           mx->rowtype_ = dict_create_var_assert (default_dict,
-                                                   "ROWTYPE_", 8);
-           mx->rowtype_->p.mxd.vartype = MXD_ROWTYPE;
-           mx->rowtype_->p.mxd.subtype = 0;
-         }
+          mx->rowtype_ = dict_create_var_assert (default_dict,
+                                                 "ROWTYPE_", 8);
+          attach_mxd_aux (mx->rowtype_, MXD_ROWTYPE, 0);
        }
       else if (lex_match_id ("FILE"))
        {
          lex_match ('=');
        }
       else if (lex_match_id ("FILE"))
        {
          lex_match ('=');
-         mx->data_file = fh_parse_file_handle ();
-         if (mx->data_file == NULL)
+         fh = fh_parse ();
+         if (fh == NULL)
            goto lossage;
        }
       else if (lex_match_id ("FORMAT"))
            goto lossage;
        }
       else if (lex_match_id ("FORMAT"))
@@ -296,10 +315,9 @@ cmd_matrix_data (void)
 
              mx->single_split = dict_create_var_assert (default_dict,
                                                          tokid, 0);
 
              mx->single_split = dict_create_var_assert (default_dict,
                                                          tokid, 0);
+              attach_mxd_aux (mx->single_split, MXD_CONTINUOUS, 0);
              lex_get ();
 
              lex_get ();
 
-             mx->single_split->p.mxd.vartype = MXD_CONTINUOUS;
-
               dict_set_split_vars (default_dict, &mx->single_split, 1);
            }
          else
               dict_set_split_vars (default_dict, &mx->single_split, 1);
            }
          else
@@ -320,14 +338,16 @@ cmd_matrix_data (void)
 
             for (i = 0; i < split_cnt; i++)
               {
 
             for (i = 0; i < split_cnt; i++)
               {
-               if (split[i]->p.mxd.vartype != MXD_CONTINUOUS)
+                struct mxd_var *mv = split[i]->aux;
+                assert (mv != NULL);
+               if (mv->var_type != MXD_CONTINUOUS)
                  {
                    msg (SE, _("Split variable %s is already another type."),
                         tokid);
                    goto lossage;
                  }
                  {
                    msg (SE, _("Split variable %s is already another type."),
                         tokid);
                    goto lossage;
                  }
-               split[i]->p.mxd.vartype = MXD_SPLIT;
-               split[i]->p.mxd.subtype = i;
+                var_clear_aux (split[i]);
+                attach_mxd_aux (split[i], MXD_SPLIT, i);
               }
          }
        }
               }
          }
        }
@@ -350,14 +370,17 @@ cmd_matrix_data (void)
            
            for (i = 0; i < mx->n_factors; i++)
              {
            
            for (i = 0; i < mx->n_factors; i++)
              {
-               if (mx->factors[i]->p.mxd.vartype != MXD_CONTINUOUS)
+                struct variable *v = mx->factors[i];
+                struct mxd_var *mv = v->aux;
+                assert (mv != NULL);
+               if (mv->var_type != MXD_CONTINUOUS)
                  {
                    msg (SE, _("Factor variable %s is already another type."),
                         tokid);
                    goto lossage;
                  }
                  {
                    msg (SE, _("Factor variable %s is already another type."),
                         tokid);
                    goto lossage;
                  }
-               mx->factors[i]->p.mxd.vartype = MXD_FACTOR;
-               mx->factors[i]->p.mxd.subtype = i;
+                var_clear_aux (v);
+                attach_mxd_aux (v, MXD_FACTOR, i);
              }
          }
        }
              }
          }
        }
@@ -537,11 +560,8 @@ cmd_matrix_data (void)
     }
       
   /* Create VARNAME_. */
     }
       
   /* Create VARNAME_. */
-  {
-    mx->varname_ = dict_create_var_assert (default_dict, "VARNAME_", 8);
-    mx->varname_->p.mxd.vartype = MXD_VARNAME;
-    mx->varname_->p.mxd.subtype = 0;
-  }
+  mx->varname_ = dict_create_var_assert (default_dict, "VARNAME_", 8);
+  attach_mxd_aux (mx->varname_, MXD_VARNAME, 0);
   
   /* Sort the dictionary variables into the desired order for the
      system file output. */
   
   /* Sort the dictionary variables into the desired order for the
      system file output. */
@@ -550,7 +570,7 @@ cmd_matrix_data (void)
     size_t nv;
 
     dict_get_vars (default_dict, &v, &nv, 0);
     size_t nv;
 
     dict_get_vars (default_dict, &v, &nv, 0);
-    qsort (v, nv, sizeof *v, compare_variables_by_mxd_vartype);
+    qsort (v, nv, sizeof *v, compare_variables_by_mxd_var_type);
     dict_reorder_vars (default_dict, v, nv);
     free (v);
   }
     dict_reorder_vars (default_dict, v, nv);
     free (v);
   }
@@ -572,7 +592,8 @@ cmd_matrix_data (void)
     for (i = 0; i < dict_get_var_cnt (default_dict); i++)
       {
        struct variable *v = dict_get_var (default_dict, i);
     for (i = 0; i < dict_get_var_cnt (default_dict); i++)
       {
        struct variable *v = dict_get_var (default_dict, i);
-       int type = v->p.mxd.vartype;
+        struct mxd_var *mv = v->aux;
+       int type = mv->var_type;
        
        assert (type >= 0 && type < MXD_COUNT);
        v->print = v->write = fmt_tab[type];
        
        assert (type >= 0 && type < MXD_COUNT);
        v->print = v->write = fmt_tab[type];
@@ -590,7 +611,8 @@ cmd_matrix_data (void)
       goto lossage;
     }
 
       goto lossage;
     }
 
-  if (!dfm_open_for_reading (mx->data_file))
+  mx->reader = dfm_open_reader (fh);
+  if (mx->reader == NULL)
     goto lossage;
 
   if (mx->explicit_rowtype)
     goto lossage;
 
   if (mx->explicit_rowtype)
@@ -598,6 +620,8 @@ cmd_matrix_data (void)
   else
     read_matrices_without_rowtype (mx);
 
   else
     read_matrices_without_rowtype (mx);
 
+  dfm_close_reader (mx->reader);
+
   pool_destroy (mx->container);
 
   return CMD_SUCCESS;
   pool_destroy (mx->container);
 
   return CMD_SUCCESS;
@@ -654,20 +678,34 @@ string_to_content_type (char *s, int *collide)
   return -1;
 }
 
   return -1;
 }
 
-/* Compare two variables using p.mxd.vartype and p.mxd.subtype
+/* Compare two variables using p.mxd.var_type and p.mxd.sub_type
    fields. */
 static int
    fields. */
 static int
-compare_variables_by_mxd_vartype (const void *a_, const void *b_)
+compare_variables_by_mxd_var_type (const void *a_, const void *b_)
 {
   struct variable *const *pa = a_;
   struct variable *const *pb = b_;
 {
   struct variable *const *pa = a_;
   struct variable *const *pb = b_;
-  const struct matrix_data_proc *a = &(*pa)->p.mxd;
-  const struct matrix_data_proc *b = &(*pb)->p.mxd;
-
-  if (a->vartype != b->vartype)
-    return a->vartype > b->vartype ? 1 : -1;
+  const struct mxd_var *a = (*pa)->aux;
+  const struct mxd_var *b = (*pb)->aux;
+  
+  if (a->var_type != b->var_type)
+    return a->var_type > b->var_type ? 1 : -1;
   else
   else
-    return a->subtype < b->subtype ? -1 : a->subtype > b->subtype;
+    return a->sub_type < b->sub_type ? -1 : a->sub_type > b->sub_type;
+}
+
+/* Attaches a struct mxd_var with the specific member values to
+   V. */
+static void
+attach_mxd_aux (struct variable *v, int var_type, int sub_type) 
+{
+  struct mxd_var *mv;
+  
+  assert (v->aux == NULL);
+  mv = xmalloc (sizeof *mv);
+  mv->var_type = var_type;
+  mv->sub_type = sub_type;
+  var_attach_aux (v, mv, var_dtor_free);
 }
 \f
 /* Matrix tokenizer. */
 }
 \f
 /* Matrix tokenizer. */
@@ -688,10 +726,10 @@ struct matrix_token
     int length;          /* MSTR: tokstr length. */
   };
 
     int length;          /* MSTR: tokstr length. */
   };
 
-static int mget_token (struct matrix_token *, struct file_handle *);
+static int mget_token (struct matrix_token *, struct dfm_reader *);
 
 #if DEBUGGING
 
 #if DEBUGGING
-#define mget_token(TOKEN, HANDLE) mget_token_dump(TOKEN, HANDLE)
+#define mget_token(TOKEN, READER) mget_token_dump(TOKEN, READER)
 
 static void
 mdump_token (const struct matrix_token *token)
 
 static void
 mdump_token (const struct matrix_token *token)
@@ -711,28 +749,28 @@ mdump_token (const struct matrix_token *token)
 }
 
 static int
 }
 
 static int
-mget_token_dump (struct matrix_token *token, struct file_handle *data_file)
+mget_token_dump (struct matrix_token *token, struct dfm_reader *reader)
 {
 {
-  int result = (mget_token) (token, data_file);
+  int result = (mget_token) (token, reader);
   mdump_token (token);
   return result;
 }
 #endif
 
   mdump_token (token);
   return result;
 }
 #endif
 
-/* Return the current position in DATA_FILE. */
+/* Return the current position in READER. */
 static const char *
 static const char *
-context (struct file_handle *data_file)
+context (struct dfm_reader *reader)
 {
   static char buf[32];
 
 {
   static char buf[32];
 
-  if (dfm_eof (data_file))
+  if (dfm_eof (reader))
     strcpy (buf, "at end of file");
   else 
     {
       struct len_string line;
       const char *sp;
       
     strcpy (buf, "at end of file");
   else 
     {
       struct len_string line;
       const char *sp;
       
-      dfm_get_record (data_file, &line);
+      dfm_get_record (reader, &line);
       sp = ls_c_str (&line);
       while (sp < ls_end (&line) && isspace ((unsigned char) *sp))
         sp++;
       sp = ls_c_str (&line);
       while (sp < ls_end (&line) && isspace ((unsigned char) *sp))
         sp++;
@@ -759,16 +797,16 @@ context (struct file_handle *data_file)
 
 /* Is there at least one token left in the data file? */
 static int
 
 /* Is there at least one token left in the data file? */
 static int
-another_token (struct file_handle *data_file)
+another_token (struct dfm_reader *reader)
 {
   for (;;)
     {
       struct len_string line;
       const char *cp;
       
 {
   for (;;)
     {
       struct len_string line;
       const char *cp;
       
-      if (dfm_eof (data_file))
+      if (dfm_eof (reader))
         return 0;
         return 0;
-      dfm_get_record (data_file, &line);
+      dfm_get_record (reader, &line);
 
       cp = ls_c_str (&line);
       while (isspace ((unsigned char) *cp) && cp < ls_end (&line))
 
       cp = ls_c_str (&line);
       while (isspace ((unsigned char) *cp) && cp < ls_end (&line))
@@ -776,27 +814,27 @@ another_token (struct file_handle *data_file)
 
       if (cp < ls_end (&line)) 
         {
 
       if (cp < ls_end (&line)) 
         {
-          dfm_forward_columns (data_file, cp - ls_c_str (&line));
+          dfm_forward_columns (reader, cp - ls_c_str (&line));
           return 1;
         }
 
           return 1;
         }
 
-      dfm_forward_record (data_file);
+      dfm_forward_record (reader);
     }
 }
 
     }
 }
 
-/* Parse a MATRIX DATA token from mx->data_file into TOKEN. */
+/* Parse a MATRIX DATA token from READER into TOKEN. */
 static int
 static int
-(mget_token) (struct matrix_token *token, struct file_handle *data_file)
+(mget_token) (struct matrix_token *token, struct dfm_reader *reader)
 {
   struct len_string line;
   int first_column;
   char *cp;
 
 {
   struct len_string line;
   int first_column;
   char *cp;
 
-  if (!another_token (data_file))
+  if (!another_token (reader))
     return 0;
 
     return 0;
 
-  dfm_get_record (data_file, &line);
-  first_column = dfm_column_start (data_file);
+  dfm_get_record (reader, &line);
+  first_column = dfm_column_start (reader);
 
   /* Three types of fields: quoted with ', quoted with ", unquoted. */
   cp = ls_c_str (&line);
 
   /* Three types of fields: quoted with ', quoted with ", unquoted. */
   cp = ls_c_str (&line);
@@ -856,22 +894,22 @@ static int
        token->type = MSTR;
     }
 
        token->type = MSTR;
     }
 
-  dfm_forward_columns (data_file, cp - ls_c_str (&line));
+  dfm_forward_columns (reader, cp - ls_c_str (&line));
     
   return 1;
 }
 
 /* Forcibly skip the end of a line for content type CONTENT in
     
   return 1;
 }
 
 /* Forcibly skip the end of a line for content type CONTENT in
-   DATA_FILE. */
+   READER. */
 static int
 static int
-force_eol (struct file_handle *data_file, const char *content)
+force_eol (struct dfm_reader *reader, const char *content)
 {
   struct len_string line;
   const char *cp;
 
 {
   struct len_string line;
   const char *cp;
 
-  if (dfm_eof (data_file))
+  if (dfm_eof (reader))
     return 0;
     return 0;
-  dfm_get_record (data_file, &line);
+  dfm_get_record (reader, &line);
 
   cp = ls_c_str (&line);
   while (isspace ((unsigned char) *cp) && cp < ls_end (&line))
 
   cp = ls_c_str (&line);
   while (isspace ((unsigned char) *cp) && cp < ls_end (&line))
@@ -880,11 +918,11 @@ force_eol (struct file_handle *data_file, const char *content)
   if (cp < ls_end (&line))
     {
       msg (SE, _("End of line expected %s while reading %s."),
   if (cp < ls_end (&line))
     {
       msg (SE, _("End of line expected %s while reading %s."),
-          context (data_file), content);
+          context (reader), content);
       return 0;
     }
   
       return 0;
     }
   
-  dfm_forward_record (data_file);
+  dfm_forward_record (reader);
   return 1;
 }
 \f
   return 1;
 }
 \f
@@ -932,8 +970,6 @@ read_matrices_without_rowtype (struct matrix_data_pgm *mx)
 
   free (nr.split_values);
   free (nr.factor_values);
 
   free (nr.split_values);
   free (nr.factor_values);
-
-  fh_close_handle (mx->data_file);
 }
 
 /* Mirror data across the diagonal of matrix CP which contains
 }
 
 /* Mirror data across the diagonal of matrix CP which contains
@@ -1063,20 +1099,20 @@ nr_read_data_lines (struct nr_aux_data *nr,
        for (j = 0; j < n_cols; j++)
          {
             struct matrix_token token;
        for (j = 0; j < n_cols; j++)
          {
             struct matrix_token token;
-           if (!mget_token (&token, mx->data_file))
+           if (!mget_token (&token, mx->reader))
              return 0;
            if (token.type != MNUM)
              {
                msg (SE, _("expecting value for %s %s"),
                     dict_get_var (default_dict, j)->name,
              return 0;
            if (token.type != MNUM)
              {
                msg (SE, _("expecting value for %s %s"),
                     dict_get_var (default_dict, j)->name,
-                     context (mx->data_file));
+                     context (mx->reader));
                return 0;
              }
 
            *cp++ = token.number;
          }
        if (mx->fmt != FREE
                return 0;
              }
 
            *cp++ = token.number;
          }
        if (mx->fmt != FREE
-            && !force_eol (mx->data_file, content_names[content]))
+            && !force_eol (mx->reader, content_names[content]))
          return 0;
        debug_printf (("\n"));
       }
          return 0;
        debug_printf (("\n"));
       }
@@ -1185,7 +1221,7 @@ matrix_data_read_without_rowtype (struct case_source *source,
       nr_output_data (nr, c, write_case, wc_data);
 
       if (dict_get_split_cnt (default_dict) == 0
       nr_output_data (nr, c, write_case, wc_data);
 
       if (dict_get_split_cnt (default_dict) == 0
-          || !another_token (mx->data_file))
+          || !another_token (mx->reader))
        return;
     }
 }
        return;
     }
 }
@@ -1212,9 +1248,11 @@ nr_read_splits (struct nr_aux_data *nr, int compare)
 
   if (mx->single_split)
     {
 
   if (mx->single_split)
     {
-      if (!compare)
-       nr->split_values[0]
-          = ++dict_get_split_vars (default_dict)[0]->p.mxd.subtype;
+      if (!compare) 
+        {
+          struct mxd_var *mv = dict_get_split_vars (default_dict)[0]->aux;
+          nr->split_values[0] = ++mv->sub_type; 
+        }
       return 1;
     }
 
       return 1;
     }
 
@@ -1225,12 +1263,12 @@ nr_read_splits (struct nr_aux_data *nr, int compare)
   for (i = 0; i < split_cnt; i++) 
     {
       struct matrix_token token;
   for (i = 0; i < split_cnt; i++) 
     {
       struct matrix_token token;
-      if (!mget_token (&token, mx->data_file))
+      if (!mget_token (&token, mx->reader))
         return 0;
       if (token.type != MNUM)
         {
           msg (SE, _("Syntax error expecting SPLIT FILE value %s."),
         return 0;
       if (token.type != MNUM)
         {
           msg (SE, _("Syntax error expecting SPLIT FILE value %s."),
-               context (mx->data_file));
+               context (mx->reader));
           return 0;
         }
 
           return 0;
         }
 
@@ -1275,12 +1313,12 @@ nr_read_factors (struct nr_aux_data *nr, int cell)
     for (i = 0; i < mx->n_factors; i++)
       {
         struct matrix_token token;
     for (i = 0; i < mx->n_factors; i++)
       {
         struct matrix_token token;
-       if (!mget_token (&token, mx->data_file))
+       if (!mget_token (&token, mx->reader))
          return 0;
        if (token.type != MNUM)
          {
            msg (SE, _("Syntax error expecting factor value %s."),
          return 0;
        if (token.type != MNUM)
          {
            msg (SE, _("Syntax error expecting factor value %s."),
-                context (mx->data_file));
+                context (mx->reader));
            return 0;
          }
        
            return 0;
          }
        
@@ -1290,7 +1328,7 @@ nr_read_factors (struct nr_aux_data *nr, int cell)
          {
            msg (SE, _("Syntax error expecting value %g for %s %s."),
                 nr->factor_values[i + mx->n_factors * cell],
          {
            msg (SE, _("Syntax error expecting value %g for %s %s."),
                 nr->factor_values[i + mx->n_factors * cell],
-                mx->factors[i]->name, context (mx->data_file));
+                mx->factors[i]->name, context (mx->reader));
            return 0;
          }
       }
            return 0;
          }
       }
@@ -1434,7 +1472,7 @@ static int wr_read_splits (struct wr_aux_data *, struct ccase *,
 static int wr_output_data (struct wr_aux_data *, struct ccase *,
                            write_case_func *, write_case_data);
 static int wr_read_rowtype (struct wr_aux_data *, 
 static int wr_output_data (struct wr_aux_data *, struct ccase *,
                            write_case_func *, write_case_data);
 static int wr_read_rowtype (struct wr_aux_data *, 
-                            const struct matrix_token *, struct file_handle *);
+                            const struct matrix_token *, struct dfm_reader *);
 static int wr_read_factors (struct wr_aux_data *);
 static int wr_read_indeps (struct wr_aux_data *);
 static void matrix_data_read_with_rowtype (struct case_source *,
 static int wr_read_factors (struct wr_aux_data *);
 static int wr_read_indeps (struct wr_aux_data *);
 static void matrix_data_read_with_rowtype (struct case_source *,
@@ -1461,7 +1499,6 @@ read_matrices_with_rowtype (struct matrix_data_pgm *mx)
   procedure (NULL, NULL);
 
   free (wr.split_values);
   procedure (NULL, NULL);
 
   free (wr.split_values);
-  fh_close_handle (mx->data_file);
 }
 
 /* Read from the data file and write it to the active file. */
 }
 
 /* Read from the data file and write it to the active file. */
@@ -1485,7 +1522,7 @@ matrix_data_read_with_rowtype (struct case_source *source,
       if (!wr_read_indeps (wr))
        return;
     }
       if (!wr_read_indeps (wr))
        return;
     }
-  while (another_token (mx->data_file));
+  while (another_token (mx->reader));
 
   wr_output_data (wr, c, write_case, wc_data);
 }
 
   wr_output_data (wr, c, write_case, wc_data);
 }
@@ -1520,12 +1557,12 @@ wr_read_splits (struct wr_aux_data *wr,
     for (i = 0; i < split_cnt; i++)
       {
         struct matrix_token token;
     for (i = 0; i < split_cnt; i++)
       {
         struct matrix_token token;
-       if (!mget_token (&token, mx->data_file))
+       if (!mget_token (&token, mx->reader))
          return 0;
        if (token.type != MNUM)
          {
            msg (SE, _("Syntax error %s expecting SPLIT FILE value."),
          return 0;
        if (token.type != MNUM)
          {
            msg (SE, _("Syntax error %s expecting SPLIT FILE value."),
-                context (mx->data_file));
+                context (mx->reader));
            return 0;
          }
 
            return 0;
          }
 
@@ -1680,22 +1717,22 @@ wr_output_data (struct wr_aux_data *wr,
   return 1;
 }
 
   return 1;
 }
 
-/* Sets ROWTYPE_ based on the given TOKEN read from DATA_FILE.
+/* Sets ROWTYPE_ based on the given TOKEN read from READER.
    Return success. */
 static int 
 wr_read_rowtype (struct wr_aux_data *wr,
                  const struct matrix_token *token,
    Return success. */
 static int 
 wr_read_rowtype (struct wr_aux_data *wr,
                  const struct matrix_token *token,
-                 struct file_handle *data_file)
+                 struct dfm_reader *reader)
 {
   if (wr->content != -1)
     {
 {
   if (wr->content != -1)
     {
-      msg (SE, _("Multiply specified ROWTYPE_ %s."), context (data_file));
+      msg (SE, _("Multiply specified ROWTYPE_ %s."), context (reader));
       return 0;
     }
   if (token->type != MSTR)
     {
       msg (SE, _("Syntax error %s expecting ROWTYPE_ string."),
       return 0;
     }
   if (token->type != MSTR)
     {
       msg (SE, _("Syntax error %s expecting ROWTYPE_ string."),
-           context (data_file));
+           context (reader));
       return 0;
     }
   
       return 0;
     }
   
@@ -1714,7 +1751,7 @@ wr_read_rowtype (struct wr_aux_data *wr,
 
   if (wr->content == -1)
     {
 
   if (wr->content == -1)
     {
-      msg (SE, _("Syntax error %s."), context (data_file));
+      msg (SE, _("Syntax error %s."), context (reader));
       return 0;
     }
 
       return 0;
     }
 
@@ -1736,19 +1773,19 @@ wr_read_factors (struct wr_aux_data *wr)
     for (i = 0; i < mx->n_factors; i++)
       {
         struct matrix_token token;
     for (i = 0; i < mx->n_factors; i++)
       {
         struct matrix_token token;
-       if (!mget_token (&token, mx->data_file))
+       if (!mget_token (&token, mx->reader))
          goto lossage;
        if (token.type == MSTR)
          {
          goto lossage;
        if (token.type == MSTR)
          {
-           if (!wr_read_rowtype (wr, &token, mx->data_file))
+           if (!wr_read_rowtype (wr, &token, mx->reader))
              goto lossage;
              goto lossage;
-           if (!mget_token (&token, mx->data_file))
+           if (!mget_token (&token, mx->reader))
              goto lossage;
          }
        if (token.type != MNUM)
          {
            msg (SE, _("Syntax error expecting factor value %s."),
              goto lossage;
          }
        if (token.type != MNUM)
          {
            msg (SE, _("Syntax error expecting factor value %s."),
-                context (mx->data_file));
+                context (mx->reader));
            goto lossage;
          }
        
            goto lossage;
          }
        
@@ -1758,9 +1795,9 @@ wr_read_factors (struct wr_aux_data *wr)
   if (wr->content == -1)
     {
       struct matrix_token token;
   if (wr->content == -1)
     {
       struct matrix_token token;
-      if (!mget_token (&token, mx->data_file))
+      if (!mget_token (&token, mx->reader))
        goto lossage;
        goto lossage;
-      if (!wr_read_rowtype (wr, &token, mx->data_file))
+      if (!wr_read_rowtype (wr, &token, mx->reader))
        goto lossage;
     }
   
        goto lossage;
     }
   
@@ -1920,20 +1957,20 @@ wr_read_indeps (struct wr_aux_data *wr)
     for (j = 0; j < n_cols; j++)
       {
         struct matrix_token token;
     for (j = 0; j < n_cols; j++)
       {
         struct matrix_token token;
-       if (!mget_token (&token, mx->data_file))
+       if (!mget_token (&token, mx->reader))
          return 0;
        if (token.type != MNUM)
          {
            msg (SE, _("Syntax error expecting value for %s %s."),
                  dict_get_var (default_dict, mx->first_continuous + j)->name,
          return 0;
        if (token.type != MNUM)
          {
            msg (SE, _("Syntax error expecting value for %s %s."),
                  dict_get_var (default_dict, mx->first_continuous + j)->name,
-                 context (mx->data_file));
+                 context (mx->reader));
            return 0;
          }
 
        *cp++ = token.number;
       }
     if (mx->fmt != FREE
            return 0;
          }
 
        *cp++ = token.number;
       }
     if (mx->fmt != FREE
-        && !force_eol (mx->data_file, content_names[wr->content]))
+        && !force_eol (mx->reader, content_names[wr->content]))
       return 0;
     debug_printf (("\n"));
   }
       return 0;
     debug_printf (("\n"));
   }
@@ -1959,3 +1996,4 @@ matrix_data_without_rowtype_source_class =
     matrix_data_read_without_rowtype,
     NULL,
   };
     matrix_data_read_without_rowtype,
     NULL,
   };
+
index 483b6dc6d45a1f3b5e9888e81672349e91395f40..7dca14b2ffdb364c87570fc415ee75312645a30f 100644 (file)
@@ -20,6 +20,7 @@
 #include <config.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <config.h>
 #include <stdlib.h>
 #include <stdio.h>
+#include "dictionary.h"
 #include "error.h"
 #include "alloc.h"
 #include "command.h"
 #include "error.h"
 #include "alloc.h"
 #include "command.h"
@@ -35,8 +36,6 @@
 /* (specification)
    means (mns_):
      *tables=custom;
 /* (specification)
    means (mns_):
      *tables=custom;
-     +variables=custom;
-     +crossbreak=custom;
      +format=lab:!labels/nolabels/nocatlabs,
             name:!names/nonames,
             val:!values/novalues,
      +format=lab:!labels/nolabels/nocatlabs,
             name:!names/nonames,
             val:!values/novalues,
@@ -130,17 +129,14 @@ mns_custom_tables (struct cmd_means *cmd)
     return 2;
   lex_match ('=');
 
     return 2;
   lex_match ('=');
 
-  if (cmd->sbc_tables || cmd->sbc_crossbreak)
+  if (cmd->sbc_tables)
     {
     {
-      msg (SE, _("TABLES or CROSSBREAK subcommand may not appear more "
+      msg (SE, _("TABLES subcommand may not appear more "
                 "than once."));
       return 0;
     }
 
                 "than once."));
       return 0;
     }
 
-  if (cmd->sbc_variables) 
-    var_set = var_set_create_from_array (v_var, n_var);
-  else
-    var_set = var_set_create_from_dict (default_dict);
+  var_set = var_set_create_from_dict (default_dict);
   assert (var_set != NULL);
 
   do
   assert (var_set != NULL);
 
   do
@@ -158,48 +154,6 @@ mns_custom_tables (struct cmd_means *cmd)
 
       nv_dim[n_dim - 1] = nvl;
       v_dim[n_dim - 1] = vl;
 
       nv_dim[n_dim - 1] = nvl;
       v_dim[n_dim - 1] = vl;
-
-      if (cmd->sbc_variables)
-       {
-         int i;
-
-         for (i = 0; i < nv_dim[0]; i++)
-           {
-             struct means_proc *v_inf = &v_dim[0][i]->p.mns;
-
-             if (v_inf->min == SYSMIS)
-               {
-                 msg (SE, _("Variable %s specified on TABLES or "
-                            "CROSSBREAK, but not specified on "
-                            "VARIABLES."),
-                      v_dim[0][i]->name);
-                  goto lossage;
-               }
-             
-             if (n_dim == 1)
-               {
-                 v_inf->min = (int) v_inf->min;
-                 v_inf->max = (int) v_inf->max;
-               } else {
-                 if (v_inf->min == LOWEST || v_inf->max == HIGHEST)
-                   {
-                     msg (SE, _("LOWEST and HIGHEST may not be used "
-                                "for independent variables (%s)."),
-                          v_dim[0][i]->name);
-                      goto lossage;
-                   }
-                 if (v_inf->min != (int) v_inf->min
-                     || v_inf->max != (int) v_inf->max)
-                   {
-                     msg (SE, _("Independent variables (%s) may not "
-                                "have noninteger endpoints in their "
-                                "ranges."),
-                          v_dim[0][i]->name);
-                      goto lossage;
-                   }
-               }
-           }
-       }
     }
   while (lex_match (T_BY));
 
     }
   while (lex_match (T_BY));
 
@@ -211,98 +165,6 @@ mns_custom_tables (struct cmd_means *cmd)
   return 0;
 }
 
   return 0;
 }
 
-/* Parse CROSSBREAK subcommand. */
-static int
-mns_custom_crossbreak (struct cmd_means *cmd)
-{
-  return mns_custom_tables (cmd);
-}
-
-/* Parses the VARIABLES subcommand. */
-static int
-mns_custom_variables (struct cmd_means *cmd)
-{
-  if (cmd->sbc_tables)
-    {
-      msg (SE, _("VARIABLES must precede TABLES."));
-      return 0;
-    }
-
-  if (cmd->sbc_variables == 1)
-    {
-      int i;
-      
-      for (i = 0; i < dict_get_var_cnt (default_dict); i++)
-       dict_get_var (default_dict, i)->p.mns.min = SYSMIS;
-    }
-  
-  do
-    {
-      int orig_n = n_var;
-      
-      double min, max;
-      
-      if (!parse_variables (default_dict, &v_var, &n_var,
-                           PV_APPEND | PV_NO_DUPLICATE | PV_NO_SCRATCH))
-       return 0;
-
-      if (!lex_force_match ('('))
-       return 0;
-
-      /* Lower value. */
-      if (token == T_ID
-         && (!strcmp (tokid, "LO") || lex_id_match ("LOWEST", tokid)))
-       min = LOWEST;
-      else
-       {
-         if (!lex_force_num ())
-           return 0;
-         min = tokval;
-       }
-      lex_get ();
-
-      lex_match (',');
-
-      /* Higher value. */
-      if (token == T_ID
-         && (!strcmp (tokid, "HI") || lex_id_match ("HIGHEST", tokid)))
-       max = HIGHEST;
-      else
-       {
-         if (!lex_force_num ())
-           return 0;
-         max = tokval;
-       }
-      lex_get ();
-
-      if (!lex_force_match (')'))
-       return 0;
-
-      /* Range check. */
-      if (max < min)
-       {
-         msg (SE, _("Upper value (%g) is less than lower value "
-                    "(%g) on VARIABLES subcommand."), max, min);
-         return 0;
-       }
-      
-      {
-       int i;
-
-       for (i = orig_n; i < n_var; i++)
-         {
-           struct means_proc *v_inf = &v_var[i]->p.mns;
-
-           v_inf->min = min;
-           v_inf->max = max;
-         }
-      }
-    }
-  while (token != '/' && token != '.');
-  
-  return 1;
-}
-
 /* 
    Local Variables:
    mode: c
 /* 
    Local Variables:
    mode: c
index aab05c6e0e029083241123c0cfa3e19e5c82cc89..5cddfe15a349e3640e0a17e26d5a32de84871148 100644 (file)
@@ -24,6 +24,7 @@
 #include "alloc.h"
 #include "bitvector.h"
 #include "command.h"
 #include "alloc.h"
 #include "bitvector.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "hash.h"
 #include "lexer.h"
 #include "error.h"
 #include "hash.h"
 #include "lexer.h"
index 26961289d5afd7eebd278b591823613afea63a75..ee8ec552ef60ebd29213ee8fd526802222c6f637 100644 (file)
@@ -21,6 +21,7 @@
 #include "error.h"
 #include <stdlib.h>
 #include "command.h"
 #include "error.h"
 #include <stdlib.h>
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "lexer.h"
 #include "str.h"
 #include "error.h"
 #include "lexer.h"
 #include "str.h"
index bcc75c7e8c23fccbcbe64044080f2a1306484f76..390ce029e47f0c907f0c86f9f00b50bcec1684ef 100644 (file)
@@ -27,6 +27,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "alloc.h"
 #include "str.h"
 #include "case.h"
 #include "alloc.h"
 #include "str.h"
 #include "case.h"
+#include "dictionary.h"
 #include "command.h"
 #include "lexer.h"
 #include "error.h"
 #include "command.h"
 #include "lexer.h"
 #include "error.h"
@@ -42,6 +43,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 #include "group_proc.h"
 #include "group.h"
 #include "levene.h"
 #include "group_proc.h"
 #include "group.h"
 #include "levene.h"
+/* (headers) */
 
 /* (specification)
    "ONEWAY" (oneway_):
 
 /* (specification)
    "ONEWAY" (oneway_):
@@ -195,7 +197,7 @@ output_oneway(void)
   /* Clean up */
   for (i = 0 ; i < n_vars ; ++i ) 
     {
   /* Clean up */
   for (i = 0 ; i < n_vars ; ++i ) 
     {
-      struct hsh_table *group_hash = vars[i]->p.grp_data.group_hash;
+      struct hsh_table *group_hash = group_proc_get (vars[i])->group_hash;
 
       hsh_destroy(group_hash);
     }
 
       hsh_destroy(group_hash);
     }
@@ -281,8 +283,8 @@ show_anova_table(void)
 
   for ( i=0 ; i < n_vars ; ++i ) 
     {
 
   for ( i=0 ; i < n_vars ; ++i ) 
     {
-      struct group_statistics *totals = &vars[i]->p.grp_data.ugs;
-      struct hsh_table *group_hash = vars[i]->p.grp_data.group_hash;
+      struct group_statistics *totals = &group_proc_get (vars[i])->ugs;
+      struct hsh_table *group_hash = group_proc_get (vars[i])->group_hash;
       struct hsh_iterator g;
       struct group_statistics *gs;
       double ssa=0;
       struct hsh_iterator g;
       struct group_statistics *gs;
       double ssa=0;
@@ -308,12 +310,13 @@ show_anova_table(void)
        tab_hline(t, TAL_1, 0, n_cols - 1 , i * 3 + 1);
 
       {
        tab_hline(t, TAL_1, 0, n_cols - 1 , i * 3 + 1);
 
       {
+        struct group_proc *gp = group_proc_get (vars[i]);
        const double sst = totals->ssq - ( totals->sum * totals->sum) / totals->n ;
        const double sst = totals->ssq - ( totals->sum * totals->sum) / totals->n ;
-       const double df1 = vars[i]->p.grp_data.n_groups - 1;
-       const double df2 = totals->n - vars[i]->p.grp_data.n_groups ;
+       const double df1 = gp->n_groups - 1;
+       const double df2 = totals->n - gp->n_groups ;
        const double msa = ssa / df1;
        
        const double msa = ssa / df1;
        
-       vars[i]->p.grp_data.mse  = (sst - ssa) / df2;
+       gp->mse  = (sst - ssa) / df2;
        
        
        /* Sums of Squares */
        
        
        /* Sums of Squares */
@@ -329,11 +332,11 @@ show_anova_table(void)
 
        /* Mean Squares */
        tab_float (t, 4, i * 3 + 1, TAB_RIGHT, msa, 8, 3);
 
        /* Mean Squares */
        tab_float (t, 4, i * 3 + 1, TAB_RIGHT, msa, 8, 3);
-       tab_float (t, 4, i * 3 + 2, TAB_RIGHT, vars[i]->p.grp_data.mse, 8, 3);
+       tab_float (t, 4, i * 3 + 2, TAB_RIGHT, gp->mse, 8, 3);
        
 
        { 
        
 
        { 
-         const double F = msa/vars[i]->p.grp_data.mse ;
+         const double F = msa/gp->mse ;
 
          /* The F value */
          tab_float (t, 5, i * 3 + 1, 0,  F, 8, 3);
 
          /* The F value */
          tab_float (t, 5, i * 3 + 1, 0,  F, 8, 3);
@@ -371,7 +374,7 @@ show_descriptives(void)
 
 
   for ( v = 0 ; v < n_vars ; ++v ) 
 
 
   for ( v = 0 ; v < n_vars ; ++v ) 
-    n_rows += vars[v]->p.grp_data.n_groups + 1;
+    n_rows += group_proc_get (vars[v])->n_groups + 1;
 
   t = tab_create (n_cols,n_rows,0);
   tab_headers (t, 2, 0, 2, 0);
 
   t = tab_create (n_cols,n_rows,0);
   tab_headers (t, 2, 0, 2, 0);
@@ -414,16 +417,17 @@ show_descriptives(void)
     {
       double T;
       double std_error;
     {
       double T;
       double std_error;
-
+      
+      struct group_proc *gp = group_proc_get (vars[v]);
 
       struct hsh_iterator g;
       struct group_statistics *gs;
 
       struct hsh_iterator g;
       struct group_statistics *gs;
-      struct group_statistics *totals = &vars[v]->p.grp_data.ugs; 
+      struct group_statistics *totals = &gp->ugs; 
 
       int count = 0 ;      
       const char *s = var_to_string(vars[v]);
 
 
       int count = 0 ;      
       const char *s = var_to_string(vars[v]);
 
-      struct hsh_table *group_hash = vars[v]->p.grp_data.group_hash;
+      struct hsh_table *group_hash = gp->group_hash;
 
 
       tab_text (t, 0, row, TAB_LEFT | TAT_TITLE, s);
 
 
       tab_text (t, 0, row, TAB_LEFT | TAT_TITLE, s);
@@ -506,7 +510,7 @@ show_descriptives(void)
       tab_float(t, 8, row + count, 0,  totals->minimum, 8, 2); 
       tab_float(t, 9, row + count, 0,  totals->maximum, 8, 2); 
 
       tab_float(t, 8, row + count, 0,  totals->minimum, 8, 2); 
       tab_float(t, 9, row + count, 0,  totals->maximum, 8, 2); 
 
-      row += vars[v]->p.grp_data.n_groups + 1;
+      row += gp->n_groups + 1;
     }
 
 
     }
 
 
@@ -554,15 +558,16 @@ show_homogeneity(void)
     {
       double F;
       const struct variable *var = vars[v];
     {
       double F;
       const struct variable *var = vars[v];
+      const struct group_proc *gp = group_proc_get (vars[v]);
       const char *s = var_to_string(var);
       const char *s = var_to_string(var);
-      const struct group_statistics *totals = &var->p.grp_data.ugs;
+      const struct group_statistics *totals = &gp->ugs;
 
 
-      const double df1 = var->p.grp_data.n_groups - 1;
-      const double df2 = totals->n - var->p.grp_data.n_groups ;
+      const double df1 = gp->n_groups - 1;
+      const double df2 = totals->n - gp->n_groups ;
 
       tab_text (t, 0, v + 1, TAB_LEFT | TAT_TITLE, s);
 
 
       tab_text (t, 0, v + 1, TAB_LEFT | TAT_TITLE, s);
 
-      F = var->p.grp_data.levene;
+      F = gp->levene;
       tab_float (t, 1, v + 1, TAB_RIGHT, F, 8,3);
       tab_float (t, 2, v + 1, TAB_RIGHT, df1 ,8,0);
       tab_float (t, 3, v + 1, TAB_RIGHT, df2 ,8,0);
       tab_float (t, 1, v + 1, TAB_RIGHT, F, 8,3);
       tab_float (t, 2, v + 1, TAB_RIGHT, df1 ,8,0);
       tab_float (t, 3, v + 1, TAB_RIGHT, df2 ,8,0);
@@ -716,7 +721,7 @@ show_contrast_tests(short *bad_contrast)
          int ci;
          double contrast_value = 0.0;
          double coef_msq = 0.0;
          int ci;
          double contrast_value = 0.0;
          double coef_msq = 0.0;
-         struct group_proc *grp_data = &vars[v]->p.grp_data ;
+         struct group_proc *grp_data = group_proc_get (vars[v]);
          struct hsh_table *group_hash = grp_data->group_hash;
          struct hsh_iterator g;
          struct group_statistics *gs;
          struct hsh_table *group_hash = grp_data->group_hash;
          struct hsh_iterator g;
          struct group_statistics *gs;
@@ -796,7 +801,7 @@ show_contrast_tests(short *bad_contrast)
                     cmd.sbc_contrast,
                     TAB_RIGHT, contrast_value, 8,2);
 
                     cmd.sbc_contrast,
                     TAB_RIGHT, contrast_value, 8,2);
 
-         std_error_contrast = sqrt(vars[v]->p.grp_data.mse * coef_msq);
+         std_error_contrast = sqrt(grp_data->mse * coef_msq);
 
          /* Std. Error */
          tab_float (t,  4, (v * lines_per_variable) + i + 1, 
 
          /* Std. Error */
          tab_float (t,  4, (v * lines_per_variable) + i + 1, 
@@ -882,13 +887,14 @@ precalc ( struct cmd_oneway *cmd UNUSED )
 
   for(i=0; i< n_vars ; ++i) 
     {
 
   for(i=0; i< n_vars ; ++i) 
     {
-      struct group_statistics *totals = &vars[i]->p.grp_data.ugs;
+      struct group_proc *gp = group_proc_get (vars[i]);
+      struct group_statistics *totals = &gp->ugs;
       
       /* Create a hash for each of the dependent variables.
         The hash contains a group_statistics structure, 
         and is keyed by value of the independent variable */
 
       
       /* Create a hash for each of the dependent variables.
         The hash contains a group_statistics structure, 
         and is keyed by value of the independent variable */
 
-      vars[i]->p.grp_data.group_hash = 
+      gp->group_hash = 
        hsh_create(4, 
                   (hsh_compare_func *) compare_group,
                   (hsh_hash_func *) hash_group,
        hsh_create(4, 
                   (hsh_compare_func *) compare_group,
                   (hsh_hash_func *) hash_group,
@@ -961,7 +967,8 @@ run_oneway(const struct casefile *cf, void *cmd_)
 
          const union value *val = case_data (&c, v->fv);
 
 
          const union value *val = case_data (&c, v->fv);
 
-         struct hsh_table *group_hash = vars[i]->p.grp_data.group_hash;
+          struct group_proc *gp = group_proc_get (vars[i]);
+         struct hsh_table *group_hash = gp->group_hash;
 
          struct group_statistics *gs;
 
 
          struct group_statistics *gs;
 
@@ -985,7 +992,7 @@ run_oneway(const struct casefile *cf, void *cmd_)
          
          if (! value_is_missing(val,v) )
            {
          
          if (! value_is_missing(val,v) )
            {
-             struct group_statistics *totals = &vars[i]->p.grp_data.ugs;
+             struct group_statistics *totals = &gp->ugs;
 
              totals->n+=weight;
              totals->sum+=weight * val->f;
 
              totals->n+=weight;
              totals->sum+=weight * val->f;
@@ -1008,7 +1015,7 @@ run_oneway(const struct casefile *cf, void *cmd_)
                gs->maximum = val->f * weight;
            }
 
                gs->maximum = val->f * weight;
            }
 
-         vars[i]->p.grp_data.n_groups = hsh_count ( group_hash );
+         gp->n_groups = hsh_count ( group_hash );
        }
   
     }
        }
   
     }
@@ -1040,8 +1047,9 @@ postcalc (  struct cmd_oneway *cmd UNUSED )
 
   for(i = 0; i < n_vars ; ++i) 
     {
 
   for(i = 0; i < n_vars ; ++i) 
     {
-      struct hsh_table *group_hash = vars[i]->p.grp_data.group_hash;
-      struct group_statistics *totals = &vars[i]->p.grp_data.ugs;
+      struct group_proc *gp = group_proc_get (vars[i]);
+      struct hsh_table *group_hash = gp->group_hash;
+      struct group_statistics *totals = &gp->ugs;
 
       struct hsh_iterator g;
       struct group_statistics *gs;
 
       struct hsh_iterator g;
       struct group_statistics *gs;
index 7be8ea05472d3defb3143c7675fac4ecb6b57e4f..e82fa00f80c2843b924c4ab6d3ff72e3ea9b0b72 100644 (file)
@@ -1,6 +1,8 @@
 /* PSPP - computes sample statistics.
    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 /* PSPP - computes sample statistics.
    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
+   Code for parsing floating-point numbers adapted from GNU C
+   library.
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
 
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
@@ -18,7 +20,7 @@
    02111-1307, USA. */
 
 #include <config.h>
    02111-1307, USA. */
 
 #include <config.h>
-#include "pfm.h"
+#include "pfm-read.h"
 #include "error.h"
 #include <stdarg.h>
 #include <stdio.h>
 #include "error.h"
 #include <stdarg.h>
 #include <stdio.h>
@@ -28,6 +30,7 @@
 #include <math.h>
 #include "alloc.h"
 #include "case.h"
 #include <math.h>
 #include "alloc.h"
 #include "case.h"
+#include "dictionary.h"
 #include "file-handle.h"
 #include "format.h"
 #include "getline.h"
 #include "file-handle.h"
 #include "format.h"
 #include "getline.h"
 
 #include "debug-print.h"
 
 
 #include "debug-print.h"
 
-/* pfm's file_handle extension. */
-struct pfm_fhuser_ext
+/* Portable file reader. */
+struct pfm_reader
   {
   {
-    FILE *file;                        /* Actual file. */
+    struct file_handle *fh;     /* File handle. */
+    FILE *file;                        /* File stream. */
 
 
-    struct dictionary *dict;   /* File's dictionary. */
     int weight_index;          /* 0-based index of weight variable, or -1. */
 
     unsigned char *trans;      /* 256-byte character set translation table. */
 
     int weight_index;          /* 0-based index of weight variable, or -1. */
 
     unsigned char *trans;      /* 256-byte character set translation table. */
 
-    int nvars;                 /* Number of variables. */
-    int *vars;                 /* Variable widths, 0 for numeric. */
-    int case_size;             /* Number of `value's per case. */
+    int var_cnt;                /* Number of variables. */
+    int *widths;                /* Variable widths, 0 for numeric. */
+    int value_cnt;             /* Number of `value's per case. */
 
     unsigned char buf[83];     /* Input buffer. */
     unsigned char *bp;         /* Buffer pointer. */
     int cc;                    /* Current character. */
   };
 
 
     unsigned char buf[83];     /* Input buffer. */
     unsigned char *bp;         /* Buffer pointer. */
     int cc;                    /* Current character. */
   };
 
-static struct fh_ext_class pfm_r_class;
-
 static int 
 static int 
-corrupt_msg (struct file_handle *h, const char *format,...)
+corrupt_msg (struct pfm_reader *r, const char *format,...)
      PRINTF_FORMAT (2, 3);
 
 /* Displays a corruption error. */
 static int
      PRINTF_FORMAT (2, 3);
 
 /* Displays a corruption error. */
 static int
-corrupt_msg (struct file_handle *h, const char *format, ...)
+corrupt_msg (struct pfm_reader *r, const char *format, ...)
 {
 {
-  struct pfm_fhuser_ext *ext = h->ext;
   char buf[1024];
   
   {
   char buf[1024];
   
   {
@@ -87,10 +87,10 @@ corrupt_msg (struct file_handle *h, const char *format, ...)
 
     e.class = ME;
     getl_location (&e.where.filename, &e.where.line_number);
 
     e.class = ME;
     getl_location (&e.where.filename, &e.where.line_number);
-    filename = handle_get_filename (h);
+    filename = handle_get_filename (r->fh);
     e.title = title = local_alloc (strlen (filename) + 80);
     sprintf (title, _("portable file %s corrupt at offset %ld: "),
     e.title = title = local_alloc (strlen (filename) + 80);
     sprintf (title, _("portable file %s corrupt at offset %ld: "),
-            filename, ftell (ext->file) - (82 - (long) (ext->bp - ext->buf)));
+            filename, ftell (r->file) - (82 - (long) (r->bp - r->buf)));
     e.text = buf;
 
     err_vmsg (&e);
     e.text = buf;
 
     err_vmsg (&e);
@@ -102,200 +102,173 @@ corrupt_msg (struct file_handle *h, const char *format, ...)
 }
 
 /* Closes a portable file after we're done with it. */
 }
 
 /* Closes a portable file after we're done with it. */
-static void
-pfm_close (struct file_handle *h)
+void
+pfm_close_reader (struct pfm_reader *r)
 {
 {
-  struct pfm_fhuser_ext *ext = h->ext;
+  if (r == NULL)
+    return;
 
 
-  if (EOF == fclose (ext->file))
+  if (r->fh != NULL)
+    fh_close (r->fh, "portable file", "rs");
+  if (fclose (r->file) == EOF)
     msg (ME, _("%s: Closing portable file: %s."),
     msg (ME, _("%s: Closing portable file: %s."),
-         handle_get_filename (h), strerror (errno));
-  free (ext->vars);
-  free (ext->trans);
-  free (h->ext);
+         handle_get_filename (r->fh), strerror (errno));
+  free (r->trans);
+  free (r);
 }
 
 }
 
-/* Displays the message X with corrupt_msg, then jumps to the lossage
+/* Displays the message X with corrupt_msg, then jumps to the error
    label. */
    label. */
-#define lose(X)                                        \
-       do                                      \
-         {                                     \
-           corrupt_msg X;                      \
-           goto lossage;                       \
-         }                                     \
-       while (0)
+#define lose(X)                                 \
+       do {                                    \
+           corrupt_msg X;                      \
+           goto error;                       \
+       } while (0)
 
 /* Read an 80-character line into handle H's buffer.  Return
    success. */
 static int
 
 /* Read an 80-character line into handle H's buffer.  Return
    success. */
 static int
-fill_buf (struct file_handle *h)
+fill_buf (struct pfm_reader *r)
 {
 {
-  struct pfm_fhuser_ext *ext = h->ext;
-
-  if (80 != fread (ext->buf, 1, 80, ext->file))
-    lose ((h, _("Unexpected end of file.")));
+  if (80 != fread (r->buf, 1, 80, r->file))
+    lose ((r, _("Unexpected end of file.")));
 
   /* PORTME: line ends. */
   {
     int c;
     
 
   /* PORTME: line ends. */
   {
     int c;
     
-    c = getc (ext->file);
+    c = getc (r->file);
     if (c != '\n' && c != '\r')
     if (c != '\n' && c != '\r')
-      lose ((h, _("Bad line end.")));
+      lose ((r, _("Bad line end.")));
 
 
-    c = getc (ext->file);
+    c = getc (r->file);
     if (c != '\n' && c != '\r')
     if (c != '\n' && c != '\r')
-      ungetc (c, ext->file);
+      ungetc (c, r->file);
   }
   
   }
   
-  if (ext->trans)
+  if (r->trans)
     {
       int i;
       
       for (i = 0; i < 80; i++)
     {
       int i;
       
       for (i = 0; i < 80; i++)
-       ext->buf[i] = ext->trans[ext->buf[i]];
+       r->buf[i] = r->trans[r->buf[i]];
     }
 
     }
 
-  ext->bp = ext->buf;
+  r->bp = r->buf;
 
   return 1;
 
 
   return 1;
 
lossage:
error:
   return 0;
 }
 
 /* Read a single character into cur_char.  Return success; */
 static int
   return 0;
 }
 
 /* Read a single character into cur_char.  Return success; */
 static int
-read_char (struct file_handle *h)
+read_char (struct pfm_reader *r)
 {
 {
-  struct pfm_fhuser_ext *ext = h->ext;
-
-  if (ext->bp >= &ext->buf[80] && !fill_buf (h))
+  if (r->bp >= &r->buf[80] && !fill_buf (r))
     return 0;
     return 0;
-  ext->cc = *ext->bp++;
+  r->cc = *r->bp++;
   return 1;
 }
 
 /* Advance a single character. */
   return 1;
 }
 
 /* Advance a single character. */
-#define advance() if (!read_char (h)) goto lossage
+#define advance()                               \
+        do {                                    \
+          if (!read_char (r))                   \
+            goto error;                       \
+        } while (0)
 
 /* Skip a single character if present, and return whether it was
    skipped. */
 static inline int
 
 /* Skip a single character if present, and return whether it was
    skipped. */
 static inline int
-skip_char (struct file_handle *h, int c)
+skip_char (struct pfm_reader *r, int c)
 {
 {
-  struct pfm_fhuser_ext *ext = h->ext;
-  
-  if (ext->cc == c)
+  if (r->cc == c)
     {
       advance ();
       return 1;
     }
     {
       advance ();
       return 1;
     }
lossage:
error:
   return 0;
 }
 
 /* Skip a single character if present, and return whether it was
    skipped. */
   return 0;
 }
 
 /* Skip a single character if present, and return whether it was
    skipped. */
-#define match(C) skip_char (h, C)
+#define match(C) skip_char (r, C)
 
 
-static int read_header (struct file_handle *h);
-static int read_version_data (struct file_handle *h, struct pfm_read_info *inf);
-static int read_variables (struct file_handle *h);
-static int read_value_label (struct file_handle *h);
-void dump_dictionary (struct dictionary *dict);
+static int read_header (struct pfm_reader *);
+static int read_version_data (struct pfm_reader *, struct pfm_read_info *);
+static int read_variables (struct pfm_reader *, struct dictionary *);
+static int read_value_label (struct pfm_reader *, struct dictionary *);
+void dump_dictionary (struct dictionary *);
 
 /* Reads the dictionary from file with handle H, and returns it in a
    dictionary structure.  This dictionary may be modified in order to
    rename, reorder, and delete variables, etc. */
 
 /* Reads the dictionary from file with handle H, and returns it in a
    dictionary structure.  This dictionary may be modified in order to
    rename, reorder, and delete variables, etc. */
-struct dictionary *
-pfm_read_dictionary (struct file_handle *h, struct pfm_read_info *inf)
+struct pfm_reader *
+pfm_open_reader (struct file_handle *fh, struct dictionary **dict,
+                 struct pfm_read_info *info)
 {
 {
-  /* The file handle extension record. */
-  struct pfm_fhuser_ext *ext;
-
-  /* Check whether the file is already open. */
-  if (h->class == &pfm_r_class)
-    {
-      ext = h->ext;
-      return ext->dict;
-    }
-  else if (h->class != NULL)
-    {
-      msg (ME, _("Cannot read file %s as portable file: already opened "
-                "for %s."),
-          handle_get_name (h), h->class->name);
-      return NULL;
-    }
-
-  msg (VM (1), _("%s: Opening portable-file handle %s for reading."),
-       handle_get_filename (h), handle_get_name (h));
-
-  /* Open the physical disk file. */
-  ext = xmalloc (sizeof (struct pfm_fhuser_ext));
-  ext->file = fopen (handle_get_filename (h), "rb");
-  if (ext->file == NULL)
+  struct pfm_reader *r = NULL;
+
+  *dict = dict_create ();
+  if (!fh_open (fh, "portable file", "rs"))
+    goto error;
+
+  /* Create and initialize reader. */
+  r = xmalloc (sizeof *r);
+  r->fh = fh;
+  r->file = fopen (handle_get_filename (r->fh), "rb");
+  r->weight_index = -1;
+  r->trans = NULL;
+  r->var_cnt = 0;
+  r->widths = NULL;
+  r->value_cnt = 0;
+  r->bp = NULL;
+
+  /* Check that file open succeeded, prime reading. */
+  if (r->file == NULL)
     {
       msg (ME, _("An error occurred while opening \"%s\" for reading "
                  "as a portable file: %s."),
     {
       msg (ME, _("An error occurred while opening \"%s\" for reading "
                  "as a portable file: %s."),
-           handle_get_filename (h), strerror (errno));
+           handle_get_filename (r->fh), strerror (errno));
       err_cond_fail ();
       err_cond_fail ();
-      free (ext);
-      return NULL;
+      goto error;
     }
     }
-
-  /* Initialize the sfm_fhuser_ext structure. */
-  h->class = &pfm_r_class;
-  h->ext = ext;
-  ext->dict = NULL;
-  ext->trans = NULL;
-  if (!fill_buf (h))
-    goto lossage;
+  if (!fill_buf (r))
+    goto error;
   advance ();
 
   advance ();
 
-  /* Read the header. */
-  if (!read_header (h))
-    goto lossage;
-  
-  /* Read version, date info, product identification. */
-  if (!read_version_data (h, inf))
-    goto lossage;
-
-  /* Read variables. */
-  if (!read_variables (h))
-    goto lossage;
+  /* Read header, version, date info, product id, variables. */
+  if (!read_header (r)
+      || !read_version_data (r, info)
+      || !read_variables (r, *dict))
+    goto error;
 
 
-  /* Value labels. */
+  /* Read value labels. */
   while (match (77 /* D */))
   while (match (77 /* D */))
-    if (!read_value_label (h))
-      goto lossage;
+    if (!read_value_label (r, *dict))
+      goto error;
 
 
+  /* Check that we've made it to the data. */
   if (!match (79 /* F */))
   if (!match (79 /* F */))
-    lose ((h, _("Data record expected.")));
-
-  msg (VM (2), _("Read portable-file dictionary successfully."));
+    lose ((r, _("Data record expected.")));
 
 
-  return ext->dict;
+  return r;
 
 
- lossage:
-  /* Come here on unsuccessful completion. */
-  msg (VM (1), _("Error reading portable-file dictionary."));
-  
-  fclose (ext->file);
-  if (ext && ext->dict)
-    dict_destroy (ext->dict);
-  free (ext);
-  h->class = NULL;
-  h->ext = NULL;
+ error:
+  pfm_close_reader (r);
+  dict_destroy (*dict);
+  *dict = NULL;
   return NULL;
 }
 \f
 /* Read a floating point value and return its value, or
    second_lowest_value on error. */
 static double
   return NULL;
 }
 \f
 /* Read a floating point value and return its value, or
    second_lowest_value on error. */
 static double
-read_float (struct file_handle *h)
+read_float (struct pfm_reader *r)
 {
 {
-  struct pfm_fhuser_ext *ext = h->ext;                       
   double num = 0.;
   int got_dot = 0;
   int got_digit = 0;
   double num = 0.;
   int got_dot = 0;
   int got_digit = 0;
@@ -316,7 +289,7 @@ read_float (struct file_handle *h)
 
   for (;;)
     {
 
   for (;;)
     {
-      if (ext->cc >= 64 /* 0 */ && ext->cc <= 93 /* T */)
+      if (r->cc >= 64 /* 0 */ && r->cc <= 93 /* T */)
        {
          got_digit++;
 
        {
          got_digit++;
 
@@ -331,14 +304,14 @@ read_float (struct file_handle *h)
               digit so that we can multiply by 10 later.  */
            ++exponent;
          else
               digit so that we can multiply by 10 later.  */
            ++exponent;
          else
-           num = (num * 30.0) + (ext->cc - 64);
+           num = (num * 30.0) + (r->cc - 64);
 
          /* Keep track of the number of digits after the decimal point.
             If we just divided by 30 here, we would lose precision.  */
          if (got_dot)
            --exponent;
        }
 
          /* Keep track of the number of digits after the decimal point.
             If we just divided by 30 here, we would lose precision.  */
          if (got_dot)
            --exponent;
        }
-      else if (!got_dot && ext->cc == 127 /* . */)
+      else if (!got_dot && r->cc == 127 /* . */)
        /* Record that we have found the decimal point.  */
        got_dot = 1;
       else
        /* Record that we have found the decimal point.  */
        got_dot = 1;
       else
@@ -349,24 +322,24 @@ read_float (struct file_handle *h)
     }
 
   if (!got_digit)
     }
 
   if (!got_digit)
-    lose ((h, "Number expected."));
+    lose ((r, "Number expected."));
       
       
-  if (ext->cc == 130 /* + */ || ext->cc == 141 /* - */)
+  if (r->cc == 130 /* + */ || r->cc == 141 /* - */)
     {
       /* Get the exponent.  */
       long int exp = 0;
     {
       /* Get the exponent.  */
       long int exp = 0;
-      int neg_exp = ext->cc == 141 /* - */;
+      int neg_exp = r->cc == 141 /* - */;
 
       for (;;)
        {
          advance ();
 
 
       for (;;)
        {
          advance ();
 
-         if (ext->cc < 64 /* 0 */ || ext->cc > 93 /* T */)
+         if (r->cc < 64 /* 0 */ || r->cc > 93 /* T */)
            break;
 
          if (exp > LONG_MAX / 30)
            goto overflow;
            break;
 
          if (exp > LONG_MAX / 30)
            goto overflow;
-         exp = exp * 30 + (ext->cc - 64);
+         exp = exp * 30 + (r->cc - 64);
        }
 
       /* We don't check whether there were actually any digits, but we
        }
 
       /* We don't check whether there were actually any digits, but we
@@ -377,7 +350,7 @@ read_float (struct file_handle *h)
     }
   
   if (!match (142 /* / */))
     }
   
   if (!match (142 /* / */))
-    lose ((h, _("Missing numeric terminator.")));
+    lose ((r, _("Missing numeric terminator.")));
 
   /* Multiply NUM by 30 to the EXPONENT power, checking for overflow.  */
 
 
   /* Multiply NUM by 30 to the EXPONENT power, checking for overflow.  */
 
@@ -401,23 +374,23 @@ read_float (struct file_handle *h)
   else
     return DBL_MAX / 10;
 
   else
     return DBL_MAX / 10;
 
lossage:
error:
   return second_lowest_value;
 }
   
 /* Read an integer and return its value, or NOT_INT on failure. */
 static int
   return second_lowest_value;
 }
   
 /* Read an integer and return its value, or NOT_INT on failure. */
 static int
-read_int (struct file_handle *h)
+read_int (struct pfm_reader *r)
 {
 {
-  double f = read_float (h);
+  double f = read_float (r);
 
   if (f == second_lowest_value)
 
   if (f == second_lowest_value)
-    goto lossage;
+    goto error;
   if (floor (f) != f || f >= INT_MAX || f <= INT_MIN)
   if (floor (f) != f || f >= INT_MAX || f <= INT_MIN)
-    lose ((h, _("Bad integer format.")));
+    lose ((r, _("Bad integer format.")));
   return f;
 
   return f;
 
lossage:
error:
   return NOT_INT;
 }
 
   return NOT_INT;
 }
 
@@ -425,13 +398,12 @@ read_int (struct file_handle *h)
    failure.  The buffer can be deallocated by calling with a NULL
    argument. */
 static unsigned char *
    failure.  The buffer can be deallocated by calling with a NULL
    argument. */
 static unsigned char *
-read_string (struct file_handle *h)
+read_string (struct pfm_reader *r)
 {
 {
-  struct pfm_fhuser_ext *ext = h->ext;
   static char *buf;
   int n;
   
   static char *buf;
   int n;
   
-  if (h == NULL)
+  if (r == NULL)
     {
       free (buf);
       buf = NULL;
     {
       free (buf);
       buf = NULL;
@@ -440,18 +412,18 @@ read_string (struct file_handle *h)
   else if (buf == NULL)
     buf = xmalloc (256);
 
   else if (buf == NULL)
     buf = xmalloc (256);
 
-  n = read_int (h);
+  n = read_int (r);
   if (n == NOT_INT)
     return NULL;
   if (n < 0 || n > 255)
   if (n == NOT_INT)
     return NULL;
   if (n < 0 || n > 255)
-    lose ((h, _("Bad string length %d."), n));
+    lose ((r, _("Bad string length %d."), n));
   
   {
     int i;
 
     for (i = 0; i < n; i++)
       {
   
   {
     int i;
 
     for (i = 0; i < n; i++)
       {
-       buf[i] = ext->cc;
+       buf[i] = r->cc;
        advance ();
       }
   }
        advance ();
       }
   }
@@ -459,16 +431,14 @@ read_string (struct file_handle *h)
   buf[n] = 0;
   return buf;
 
   buf[n] = 0;
   return buf;
 
lossage:
error:
   return NULL;
 }
 \f
 /* Reads the 464-byte file header. */
 int
   return NULL;
 }
 \f
 /* Reads the 464-byte file header. */
 int
-read_header (struct file_handle *h)
+read_header (struct pfm_reader *r)
 {
 {
-  struct pfm_fhuser_ext *ext = h->ext;
-
   /* For now at least, just ignore the vanity splash strings. */
   {
     int i;
   /* For now at least, just ignore the vanity splash strings. */
   {
     int i;
@@ -484,7 +454,7 @@ read_header (struct file_handle *h)
 
     for (i = 0; i < 256; i++)
       {
 
     for (i = 0; i < 256; i++)
       {
-       src[i] = (unsigned char) ext->cc;
+       src[i] = (unsigned char) r->cc;
        advance ();
       }
 
        advance ();
       }
 
@@ -498,14 +468,14 @@ read_header (struct file_handle *h)
       if (trans_temp[src[i]] == -1)
        trans_temp[src[i]] = i;
     
       if (trans_temp[src[i]] == -1)
        trans_temp[src[i]] = i;
     
-    ext->trans = xmalloc (256);
+    r->trans = xmalloc (256);
     for (i = 0; i < 256; i++)
     for (i = 0; i < 256; i++)
-      ext->trans[i] = trans_temp[i] == -1 ? 0 : trans_temp[i];
+      r->trans[i] = trans_temp[i] == -1 ? 0 : trans_temp[i];
 
     /* Translate the input buffer. */
     for (i = 0; i < 80; i++)
 
     /* Translate the input buffer. */
     for (i = 0; i < 80; i++)
-      ext->buf[i] = ext->trans[ext->buf[i]];
-    ext->cc = ext->trans[ext->cc];
+      r->buf[i] = r->trans[r->buf[i]];
+    r->cc = r->trans[r->cc];
   }
   
   {
   }
   
   {
@@ -514,71 +484,69 @@ read_header (struct file_handle *h)
 
     for (i = 0; i < 8; i++)
       if (!match (sig[i]))
 
     for (i = 0; i < 8; i++)
       if (!match (sig[i]))
-       lose ((h, "Missing SPSSPORT signature."));
+       lose ((r, "Missing SPSSPORT signature."));
   }
 
   return 1;
 
   }
 
   return 1;
 
lossage:
error:
   return 0;
 }
 
 /* Reads the version and date info record, as well as product and
    subproduct identification records if present. */
 int
   return 0;
 }
 
 /* Reads the version and date info record, as well as product and
    subproduct identification records if present. */
 int
-read_version_data (struct file_handle *h, struct pfm_read_info *inf)
+read_version_data (struct pfm_reader *r, struct pfm_read_info *info)
 {
 {
-  struct pfm_fhuser_ext *ext = h->ext;
-
   /* Version. */
   if (!match (74 /* A */))
   /* Version. */
   if (!match (74 /* A */))
-    lose ((h, "Unrecognized version code %d.", ext->cc));
+    lose ((r, "Unrecognized version code %d.", r->cc));
 
   /* Date. */
   {
     static const int map[] = {6, 7, 8, 9, 3, 4, 0, 1};
 
   /* Date. */
   {
     static const int map[] = {6, 7, 8, 9, 3, 4, 0, 1};
-    char *date = read_string (h);
+    char *date = read_string (r);
     int i;
     
     if (!date)
       return 0;
     if (strlen (date) != 8)
     int i;
     
     if (!date)
       return 0;
     if (strlen (date) != 8)
-      lose ((h, _("Bad date string length %d."), strlen (date)));
+      lose ((r, _("Bad date string length %d."), strlen (date)));
     for (i = 0; i < 8; i++)
       {
        if (date[i] < 64 /* 0 */ || date[i] > 73 /* 9 */)
     for (i = 0; i < 8; i++)
       {
        if (date[i] < 64 /* 0 */ || date[i] > 73 /* 9 */)
-         lose ((h, _("Bad character in date.")));
-       if (inf)
-         inf->creation_date[map[i]] = date[i] - 64 /* 0 */ + '0';
+         lose ((r, _("Bad character in date.")));
+       if (info)
+         info->creation_date[map[i]] = date[i] - 64 /* 0 */ + '0';
       }
       }
-    if (inf)
+    if (info)
       {
       {
-       inf->creation_date[2] = inf->creation_date[5] = ' ';
-       inf->creation_date[10] = 0;
+       info->creation_date[2] = info->creation_date[5] = ' ';
+       info->creation_date[10] = 0;
       }
   }
   
   /* Time. */
   {
     static const int map[] = {0, 1, 3, 4, 6, 7};
       }
   }
   
   /* Time. */
   {
     static const int map[] = {0, 1, 3, 4, 6, 7};
-    char *time = read_string (h);
+    char *time = read_string (r);
     int i;
 
     if (!time)
       return 0;
     if (strlen (time) != 6)
     int i;
 
     if (!time)
       return 0;
     if (strlen (time) != 6)
-      lose ((h, _("Bad time string length %d."), strlen (time)));
+      lose ((r, _("Bad time string length %d."), strlen (time)));
     for (i = 0; i < 6; i++)
       {
        if (time[i] < 64 /* 0 */ || time[i] > 73 /* 9 */)
     for (i = 0; i < 6; i++)
       {
        if (time[i] < 64 /* 0 */ || time[i] > 73 /* 9 */)
-         lose ((h, _("Bad character in time.")));
-       if (inf)
-         inf->creation_time[map[i]] = time[i] - 64 /* 0 */ + '0';
+         lose ((r, _("Bad character in time.")));
+       if (info)
+         info->creation_time[map[i]] = time[i] - 64 /* 0 */ + '0';
       }
       }
-    if (inf)
+    if (info)
       {
       {
-       inf->creation_time[2] = inf->creation_time[5] = ' ';
-       inf->creation_time[8] = 0;
+       info->creation_time[2] = info->creation_time[5] = ' ';
+       info->creation_time[8] = 0;
       }
   }
 
       }
   }
 
@@ -587,57 +555,57 @@ read_version_data (struct file_handle *h, struct pfm_read_info *inf)
     {
       char *product;
       
     {
       char *product;
       
-      product = read_string (h);
+      product = read_string (r);
       if (product == NULL)
        return 0;
       if (product == NULL)
        return 0;
-      if (inf)
-       strncpy (inf->product, product, 61);
+      if (info)
+       strncpy (info->product, product, 61);
     }
     }
-  else if (inf)
-    inf->product[0] = 0;
+  else if (info)
+    info->product[0] = 0;
 
   /* Subproduct. */
   if (match (67 /* 3 */))
     {
       char *subproduct;
 
 
   /* Subproduct. */
   if (match (67 /* 3 */))
     {
       char *subproduct;
 
-      subproduct = read_string (h);
+      subproduct = read_string (r);
       if (subproduct == NULL)
        return 0;
       if (subproduct == NULL)
        return 0;
-      if (inf)
-       strncpy (inf->subproduct, subproduct, 61);
+      if (info)
+       strncpy (info->subproduct, subproduct, 61);
     }
     }
-  else if (inf)
-    inf->subproduct[0] = 0;
+  else if (info)
+    info->subproduct[0] = 0;
   return 1;
   
   return 1;
   
lossage:
error:
   return 0;
 }
 
 static int
   return 0;
 }
 
 static int
-convert_format (struct file_handle *h, int fmt[3], struct fmt_spec *v,
+convert_format (struct pfm_reader *r, int fmt[3], struct fmt_spec *v,
                struct variable *vv)
 {
   v->type = translate_fmt (fmt[0]);
   if (v->type == -1)
                struct variable *vv)
 {
   v->type = translate_fmt (fmt[0]);
   if (v->type == -1)
-    lose ((h, _("%s: Bad format specifier byte (%d)."), vv->name, fmt[0]));
+    lose ((r, _("%s: Bad format specifier byte (%d)."), vv->name, fmt[0]));
   v->w = fmt[1];
   v->d = fmt[2];
 
   /* FIXME?  Should verify the resulting specifier more thoroughly. */
 
   if (v->type == -1)
   v->w = fmt[1];
   v->d = fmt[2];
 
   /* FIXME?  Should verify the resulting specifier more thoroughly. */
 
   if (v->type == -1)
-    lose ((h, _("%s: Bad format specifier byte (%d)."), vv->name, fmt[0]));
+    lose ((r, _("%s: Bad format specifier byte (%d)."), vv->name, fmt[0]));
   if ((vv->type == ALPHA) ^ ((formats[v->type].cat & FCAT_STRING) != 0))
   if ((vv->type == ALPHA) ^ ((formats[v->type].cat & FCAT_STRING) != 0))
-    lose ((h, _("%s variable %s has %s format specifier %s."),
+    lose ((r, _("%s variable %s has %s format specifier %s."),
           vv->type == ALPHA ? _("String") : _("Numeric"),
           vv->name,
           formats[v->type].cat & FCAT_STRING ? _("string") : _("numeric"),
           formats[v->type].name));
   return 1;
 
           vv->type == ALPHA ? _("String") : _("Numeric"),
           vv->name,
           formats[v->type].cat & FCAT_STRING ? _("string") : _("numeric"),
           formats[v->type].name));
   return 1;
 
lossage:
error:
   return 0;
 }
 
   return 0;
 }
 
@@ -659,52 +627,49 @@ asciify (char *s)
     *s = spss2ascii[(unsigned char) *s];
 }
 
     *s = spss2ascii[(unsigned char) *s];
 }
 
-static int parse_value (struct file_handle *, union value *, struct variable *);
+static int parse_value (struct pfm_reader *, union value *, struct variable *);
 
 /* Read information on all the variables.  */
 static int
 
 /* Read information on all the variables.  */
 static int
-read_variables (struct file_handle *h)
+read_variables (struct pfm_reader *r, struct dictionary *dict)
 {
 {
-  struct pfm_fhuser_ext *ext = h->ext;
   char *weight_name = NULL;
   int i;
   
   if (!match (68 /* 4 */))
   char *weight_name = NULL;
   int i;
   
   if (!match (68 /* 4 */))
-    lose ((h, _("Expected variable count record.")));
+    lose ((r, _("Expected variable count record.")));
   
   
-  ext->nvars = read_int (h);
-  if (ext->nvars <= 0 || ext->nvars == NOT_INT)
-    lose ((h, _("Invalid number of variables %d."), ext->nvars));
-  ext->vars = xmalloc (sizeof *ext->vars * ext->nvars);
+  r->var_cnt = read_int (r);
+  if (r->var_cnt <= 0 || r->var_cnt == NOT_INT)
+    lose ((r, _("Invalid number of variables %d."), r->var_cnt));
+  r->widths = xmalloc (sizeof *r->widths * r->var_cnt);
 
   /* Purpose of this value is unknown.  It is typically 161. */
   {
 
   /* Purpose of this value is unknown.  It is typically 161. */
   {
-    int x = read_int (h);
+    int x = read_int (r);
 
     if (x == NOT_INT)
 
     if (x == NOT_INT)
-      goto lossage;
+      goto error;
     if (x != 161)
     if (x != 161)
-      corrupt_msg (h, _("Unexpected flag value %d."), x);
+      corrupt_msg (r, _("Unexpected flag value %d."), x);
   }
 
   }
 
-  ext->dict = dict_create ();
-
   if (match (70 /* 6 */))
     {
   if (match (70 /* 6 */))
     {
-      weight_name = read_string (h);
+      weight_name = read_string (r);
       if (!weight_name)
       if (!weight_name)
-       goto lossage;
+       goto error;
 
       asciify (weight_name);
       if (strlen (weight_name) > 8) 
         {
 
       asciify (weight_name);
       if (strlen (weight_name) > 8) 
         {
-          corrupt_msg (h, _("Weight variable name (%s) truncated."),
+          corrupt_msg (r, _("Weight variable name (%s) truncated."),
                        weight_name);
           weight_name[8] = '\0';
         }
     }
   
                        weight_name);
           weight_name[8] = '\0';
         }
     }
   
-  for (i = 0; i < ext->nvars; i++)
+  for (i = 0; i < r->var_cnt; i++)
     {
       int width;
       unsigned char *name;
     {
       int width;
       unsigned char *name;
@@ -713,23 +678,23 @@ read_variables (struct file_handle *h)
       int j;
 
       if (!match (71 /* 7 */))
       int j;
 
       if (!match (71 /* 7 */))
-       lose ((h, _("Expected variable record.")));
+       lose ((r, _("Expected variable record.")));
 
 
-      width = read_int (h);
+      width = read_int (r);
       if (width == NOT_INT)
       if (width == NOT_INT)
-       goto lossage;
+       goto error;
       if (width < 0)
       if (width < 0)
-       lose ((h, _("Invalid variable width %d."), width));
-      ext->vars[i] = width;
+       lose ((r, _("Invalid variable width %d."), width));
+      r->widths[i] = width;
       
       
-      name = read_string (h);
+      name = read_string (r);
       if (name == NULL)
       if (name == NULL)
-       goto lossage;
+       goto error;
       for (j = 0; j < 6; j++)
        {
       for (j = 0; j < 6; j++)
        {
-         fmt[j] = read_int (h);
+         fmt[j] = read_int (r);
          if (fmt[j] == NOT_INT)
          if (fmt[j] == NOT_INT)
-           goto lossage;
+           goto error;
        }
 
       /* Verify first character of variable name.
        }
 
       /* Verify first character of variable name.
@@ -737,15 +702,15 @@ read_variables (struct file_handle *h)
         Weirdly enough, there is no # character in the SPSS portable
         character set, so we can't check for it. */
       if (strlen (name) > 8)
         Weirdly enough, there is no # character in the SPSS portable
         character set, so we can't check for it. */
       if (strlen (name) > 8)
-       lose ((h, _("position %d: Variable name has %u characters."),
+       lose ((r, _("position %d: Variable name has %u characters."),
               i, strlen (name)));
       if ((name[0] < 74 /* A */ || name[0] > 125 /* Z */)
          && name[0] != 152 /* @ */)
               i, strlen (name)));
       if ((name[0] < 74 /* A */ || name[0] > 125 /* Z */)
          && name[0] != 152 /* @ */)
-       lose ((h, _("position %d: Variable name begins with invalid "
+       lose ((r, _("position %d: Variable name begins with invalid "
               "character."), i));
       if (name[0] >= 100 /* a */ && name[0] <= 125 /* z */)
        {
               "character."), i));
       if (name[0] >= 100 /* a */ && name[0] <= 125 /* z */)
        {
-         corrupt_msg (h, _("position %d: Variable name begins with "
+         corrupt_msg (r, _("position %d: Variable name begins with "
                            "lowercase letter %c."),
                       i, name[0] - 100 + 'a');
          name[0] -= 26 /* a - A */;
                            "lowercase letter %c."),
                       i, name[0] - 100 + 'a');
          name[0] -= 26 /* a - A */;
@@ -758,7 +723,7 @@ read_variables (struct file_handle *h)
 
          if (c >= 100 /* a */ && c <= 125 /* z */)
            {
 
          if (c >= 100 /* a */ && c <= 125 /* z */)
            {
-             corrupt_msg (h, _("position %d: Variable name character %d "
+             corrupt_msg (r, _("position %d: Variable name character %d "
                                "is lowercase letter %c."),
                           i, j + 1, c - 100 + 'a');
              name[j] -= 26 /* z - Z */;
                                "is lowercase letter %c."),
                           i, j + 1, c - 100 + 'a');
              name[j] -= 26 /* z - Z */;
@@ -768,42 +733,43 @@ read_variables (struct file_handle *h)
                   || c == 136 /* $ */ || c == 146 /* _ */)
            name[j] = c;
          else
                   || c == 136 /* $ */ || c == 146 /* _ */)
            name[j] = c;
          else
-           lose ((h, _("position %d: character `\\%03o' is not "
+           lose ((r, _("position %d: character `\\%03o' is not "
                        "valid in a variable name."), i, c));
        }
 
       asciify (name);
       if (width < 0 || width > 255)
                        "valid in a variable name."), i, c));
        }
 
       asciify (name);
       if (width < 0 || width > 255)
-       lose ((h, "Bad width %d for variable %s.", width, name));
+       lose ((r, "Bad width %d for variable %s.", width, name));
 
 
-      v = dict_create_var (ext->dict, name, width);
-      v->get.fv = v->fv;
+      v = dict_create_var (dict, name, width);
+      v->aux = xmalloc (sizeof (int));
+      *(int *) v->aux = v->fv;
       if (v == NULL)
       if (v == NULL)
-       lose ((h, _("Duplicate variable name %s."), name));
-      if (!convert_format (h, &fmt[0], &v->print, v))
-       goto lossage;
-      if (!convert_format (h, &fmt[3], &v->write, v))
-       goto lossage;
+       lose ((r, _("Duplicate variable name %s."), name));
+      if (!convert_format (r, &fmt[0], &v->print, v))
+       goto error;
+      if (!convert_format (r, &fmt[3], &v->write, v))
+       goto error;
 
       /* Range missing values. */
       if (match (75 /* B */))
        {
          v->miss_type = MISSING_RANGE;
 
       /* Range missing values. */
       if (match (75 /* B */))
        {
          v->miss_type = MISSING_RANGE;
-         if (!parse_value (h, &v->missing[0], v)
-             || !parse_value (h, &v->missing[1], v))
-           goto lossage;
+         if (!parse_value (r, &v->missing[0], v)
+             || !parse_value (r, &v->missing[1], v))
+           goto error;
        }
       else if (match (74 /* A */))
        {
          v->miss_type = MISSING_HIGH;
        }
       else if (match (74 /* A */))
        {
          v->miss_type = MISSING_HIGH;
-         if (!parse_value (h, &v->missing[0], v))
-           goto lossage;
+         if (!parse_value (r, &v->missing[0], v))
+           goto error;
        }
       else if (match (73 /* 9 */))
        {
          v->miss_type = MISSING_LOW;
        }
       else if (match (73 /* 9 */))
        {
          v->miss_type = MISSING_LOW;
-         if (!parse_value (h, &v->missing[0], v))
-           goto lossage;
+         if (!parse_value (r, &v->missing[0], v))
+           goto error;
        }
 
       /* Single missing values. */
        }
 
       /* Single missing values. */
@@ -823,19 +789,19 @@ read_variables (struct file_handle *h)
 
          v->miss_type = map_next[v->miss_type];
          if (v->miss_type == -1)
 
          v->miss_type = map_next[v->miss_type];
          if (v->miss_type == -1)
-           lose ((h, _("Bad missing values for %s."), v->name));
+           lose ((r, _("Bad missing values for %s."), v->name));
          
          assert (map_ofs[v->miss_type] != -1);
          
          assert (map_ofs[v->miss_type] != -1);
-         if (!parse_value (h, &v->missing[map_ofs[v->miss_type]], v))
-           goto lossage;
+         if (!parse_value (r, &v->missing[map_ofs[v->miss_type]], v))
+           goto error;
        }
 
       if (match (76 /* C */))
        {
        }
 
       if (match (76 /* C */))
        {
-         char *label = read_string (h);
+         char *label = read_string (r);
          
          if (label == NULL)
          
          if (label == NULL)
-           goto lossage;
+           goto error;
 
          v->label = xstrdup (label);
          asciify (v->label);
 
          v->label = xstrdup (label);
          asciify (v->label);
@@ -844,29 +810,29 @@ read_variables (struct file_handle *h)
 
   if (weight_name != NULL) 
     {
 
   if (weight_name != NULL) 
     {
-      struct variable *weight_var = dict_lookup_var (ext->dict, weight_name);
+      struct variable *weight_var = dict_lookup_var (dict, weight_name);
       if (weight_var == NULL)
       if (weight_var == NULL)
-        lose ((h, _("Weighting variable %s not present in dictionary."),
+        lose ((r, _("Weighting variable %s not present in dictionary."),
                weight_name));
       free (weight_name);
 
                weight_name));
       free (weight_name);
 
-      dict_set_weight (ext->dict, weight_var);
+      dict_set_weight (dict, weight_var);
     }
 
   return 1;
 
     }
 
   return 1;
 
lossage:
error:
   free (weight_name);
   return 0;
 }
 
 /* Parse a value for variable VV into value V.  Returns success. */
 static int
   free (weight_name);
   return 0;
 }
 
 /* Parse a value for variable VV into value V.  Returns success. */
 static int
-parse_value (struct file_handle *h, union value *v, struct variable *vv)
+parse_value (struct pfm_reader *r, union value *v, struct variable *vv)
 {
   if (vv->type == ALPHA)
     {
 {
   if (vv->type == ALPHA)
     {
-      char *mv = read_string (h);
+      char *mv = read_string (r);
       int j;
       
       if (mv == NULL)
       int j;
       
       if (mv == NULL)
@@ -882,7 +848,7 @@ parse_value (struct file_handle *h, union value *v, struct variable *vv)
     }
   else
     {
     }
   else
     {
-      v->f = read_float (h);
+      v->f = read_float (r);
       if (v->f == second_lowest_value)
        return 0;
     }
       if (v->f == second_lowest_value)
        return 0;
     }
@@ -892,10 +858,8 @@ parse_value (struct file_handle *h, union value *v, struct variable *vv)
 
 /* Parse a value label record and return success. */
 static int
 
 /* Parse a value label record and return success. */
 static int
-read_value_label (struct file_handle *h)
+read_value_label (struct pfm_reader *r, struct dictionary *dict)
 {
 {
-  struct pfm_fhuser_ext *ext = h->ext;
-
   /* Variables. */
   int nv;
   struct variable **v;
   /* Variables. */
   int nv;
   struct variable **v;
@@ -905,31 +869,31 @@ read_value_label (struct file_handle *h)
 
   int i;
 
 
   int i;
 
-  nv = read_int (h);
+  nv = read_int (r);
   if (nv == NOT_INT)
     return 0;
 
   v = xmalloc (sizeof *v * nv);
   for (i = 0; i < nv; i++)
     {
   if (nv == NOT_INT)
     return 0;
 
   v = xmalloc (sizeof *v * nv);
   for (i = 0; i < nv; i++)
     {
-      char *name = read_string (h);
+      char *name = read_string (r);
       if (name == NULL)
       if (name == NULL)
-       goto lossage;
+       goto error;
       asciify (name);
 
       asciify (name);
 
-      v[i] = dict_lookup_var (ext->dict, name);
+      v[i] = dict_lookup_var (dict, name);
       if (v[i] == NULL)
       if (v[i] == NULL)
-       lose ((h, _("Unknown variable %s while parsing value labels."), name));
+       lose ((r, _("Unknown variable %s while parsing value labels."), name));
 
       if (v[0]->width != v[i]->width)
 
       if (v[0]->width != v[i]->width)
-       lose ((h, _("Cannot assign value labels to %s and %s, which "
+       lose ((r, _("Cannot assign value labels to %s and %s, which "
                    "have different variable types or widths."),
               v[0]->name, v[i]->name));
     }
 
                    "have different variable types or widths."),
               v[0]->name, v[i]->name));
     }
 
-  n_labels = read_int (h);
+  n_labels = read_int (r);
   if (n_labels == NOT_INT)
   if (n_labels == NOT_INT)
-    goto lossage;
+    goto error;
 
   for (i = 0; i < n_labels; i++)
     {
 
   for (i = 0; i < n_labels; i++)
     {
@@ -938,12 +902,12 @@ read_value_label (struct file_handle *h)
 
       int j;
       
 
       int j;
       
-      if (!parse_value (h, &val, v[0]))
-       goto lossage;
+      if (!parse_value (r, &val, v[0]))
+       goto error;
       
       
-      label = read_string (h);
+      label = read_string (r);
       if (label == NULL)
       if (label == NULL)
-       goto lossage;
+       goto error;
       asciify (label);
 
       /* Assign the value_label's to each variable. */
       asciify (label);
 
       /* Assign the value_label's to each variable. */
@@ -955,90 +919,64 @@ read_value_label (struct file_handle *h)
            continue;
 
          if (var->type == NUMERIC)
            continue;
 
          if (var->type == NUMERIC)
-           lose ((h, _("Duplicate label for value %g for variable %s."),
+           lose ((r, _("Duplicate label for value %g for variable %s."),
                   val.f, var->name));
          else
                   val.f, var->name));
          else
-           lose ((h, _("Duplicate label for value `%.*s' for variable %s."),
+           lose ((r, _("Duplicate label for value `%.*s' for variable %s."),
                   var->width, val.s, var->name));
        }
     }
   free (v);
   return 1;
 
                   var->width, val.s, var->name));
        }
     }
   free (v);
   return 1;
 
lossage:
error:
   free (v);
   return 0;
 }
 
   free (v);
   return 0;
 }
 
-/* Reads one case from portable file H into PERM
-   according to the instuctions given in associated dictionary DICT,
-   which must have the get.fv elements appropriately set.  Returns
-   nonzero only if successful. */
+/* Reads one case from portable file R into C.  Returns nonzero
+   only if successful. */
 int
 int
-pfm_read_case (struct file_handle *h, struct ccase *perm,
-               struct dictionary *dict)
+pfm_read_case (struct pfm_reader *r, struct ccase *c)
 {
 {
-  struct pfm_fhuser_ext *ext = h->ext;
-
-  union value *temp, *tp;
-  int i;
+  size_t i;
+  size_t idx;
 
   /* Check for end of file. */
 
   /* Check for end of file. */
-  if (ext->cc == 99 /* Z */)
+  if (r->cc == 99 /* Z */)
     return 0;
   
     return 0;
   
-  /* The first concern is to obtain a full case relative to the data
-     file.  (Cases in the data file have no particular relationship to
-     cases in the active file.) */
-  tp = temp = local_alloc (sizeof *tp * ext->case_size);
-  for (tp = temp, i = 0; i < ext->nvars; i++)
-    if (ext->vars[i] == 0)
-      {
-       tp->f = read_float (h);
-       if (tp->f == second_lowest_value)
-         goto unexpected_eof;
-       tp++;
-      }
-    else
-      {
-       char *s = read_string (h);
-       if (s == NULL)
-         goto unexpected_eof;
-       asciify (s);
-         
-       st_bare_pad_copy (tp->s, s, ext->vars[i]);
-       tp += DIV_RND_UP (ext->vars[i], MAX_SHORT_STRING);
-      }
-
-  /* Translate a case in data file format to a case in active file
-     format. */
-  for (i = 0; i < dict_get_var_cnt (dict); i++)
+  idx = 0;
+  for (i = 0; i < r->var_cnt; i++) 
     {
     {
-      struct variable *v = dict_get_var (dict, i);
-
-      if (v->get.fv == -1)
-       continue;
+      int width = r->widths[i];
       
       
-      if (v->type == NUMERIC)
-        case_data_rw (perm, v->fv)->f = temp[v->get.fv].f;
+      if (width == 0)
+        {
+          double f = read_float (r);
+          if (f == second_lowest_value)
+            goto unexpected_eof;
+
+          case_data_rw (c, idx)->f = f;
+          idx++;
+        }
       else
       else
-       memcpy (case_data_rw (perm, v->fv)->s, &temp[v->get.fv], v->width);
-    }
+        {
+          char *s = read_string (r);
+          if (s == NULL)
+            goto unexpected_eof;
+          asciify (s);
 
 
-  local_free (temp);
+          st_bare_pad_copy (case_data_rw (c, idx)->s, s, width);
+          idx += DIV_RND_UP (width, MAX_SHORT_STRING);
+        }
+    }
+  
   return 1;
 
  unexpected_eof:
   return 1;
 
  unexpected_eof:
-  lose ((h, _("End of file midway through case.")));
+  lose ((r, _("End of file midway through case.")));
 
 
- lossage:
-  local_free (temp);
+ error:
   return 0;
 }
   return 0;
 }
-
-static struct fh_ext_class pfm_r_class =
-{
-  5,
-  N_("reading as a portable file"),
-  pfm_close,
-};
diff --git a/src/pfm-read.h b/src/pfm-read.h
new file mode 100644 (file)
index 0000000..bb37f1d
--- /dev/null
@@ -0,0 +1,51 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 1997-9, 2000 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., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#ifndef PFM_READ_H
+#define PFM_READ_H
+
+/* Portable file reading. */
+
+/* Portable file types. */
+enum pfm_type
+  {
+    PFM_COMM,   /* Formatted for communication. */
+    PFM_TAPE    /* Formatted for tape. */
+  };
+
+/* Information produced by pfm_read_dictionary() that doesn't fit into
+   a dictionary struct. */
+struct pfm_read_info
+  {
+    char creation_date[11];    /* `dd mm yyyy' plus a null. */
+    char creation_time[9];     /* `hh:mm:ss' plus a null. */
+    char product[61];          /* Product name plus a null. */
+    char subproduct[61];       /* Subproduct name plus a null. */
+  };
+
+struct dictionary;
+struct file_handle;
+struct ccase;
+struct pfm_reader *pfm_open_reader (struct file_handle *,
+                                    struct dictionary **,
+                                    struct pfm_read_info *);
+int pfm_read_case (struct pfm_reader *, struct ccase *);
+void pfm_close_reader (struct pfm_reader *);
+
+#endif /* pfm-read.h */
index 1d553ed7875d774aa5912f07125304638371c369..bdd93c99254ad2cfd210899bdf12ae70568758c2 100644 (file)
@@ -18,7 +18,7 @@
    02111-1307, USA. */
 
 #include <config.h>
    02111-1307, USA. */
 
 #include <config.h>
-#include "pfm.h"
+#include "pfm-write.h"
 #include "error.h"
 #include <ctype.h>
 #include <errno.h>
 #include "error.h"
 #include <ctype.h>
 #include <errno.h>
@@ -28,6 +28,8 @@
 #include <stdlib.h>
 #include <time.h>
 #include "alloc.h"
 #include <stdlib.h>
 #include <time.h>
 #include "alloc.h"
+#include "case.h"
+#include "dictionary.h"
 #include "error.h"
 #include "file-handle.h"
 #include "gmp.h"
 #include "error.h"
 #include "file-handle.h"
 #include "gmp.h"
 
 #include "debug-print.h"
 
 
 #include "debug-print.h"
 
-/* pfm writer file_handle extension. */
-struct pfm_fhuser_ext
+/* Portable file writer. */
+struct pfm_writer
   {
   {
-    FILE *file;                        /* Actual file. */
+    struct file_handle *fh;     /* File handle. */
+    FILE *file;                        /* File stream. */
 
     int lc;                    /* Number of characters on this line so far. */
 
 
     int lc;                    /* Number of characters on this line so far. */
 
-    int nvars;                 /* Number of variables. */
-    int *vars;                 /* Variable widths. */
+    size_t var_cnt;             /* Number of variables. */
+    struct pfm_var *vars;       /* Variables. */
   };
 
   };
 
-static struct fh_ext_class pfm_w_class;
+/* A variable to write to the portable file. */
+struct pfm_var 
+  {
+    int width;                  /* 0=numeric, otherwise string var width. */
+    int fv;                     /* Starting case index. */
+  };
 
 
-static int bufwrite (struct file_handle *h, const void *buf, size_t nbytes);
-static int write_header (struct file_handle *h);
-static int write_version_data (struct file_handle *h);
-static int write_variables (struct file_handle *h, struct dictionary *d);
-static int write_value_labels (struct file_handle *h, struct dictionary *d);
+static int buf_write (struct pfm_writer *, const void *, size_t);
+static int write_header (struct pfm_writer *);
+static int write_version_data (struct pfm_writer *);
+static int write_variables (struct pfm_writer *, const struct dictionary *);
+static int write_value_labels (struct pfm_writer *, const struct dictionary *);
 
 /* Writes the dictionary DICT to portable file HANDLE.  Returns
    nonzero only if successful. */
 
 /* Writes the dictionary DICT to portable file HANDLE.  Returns
    nonzero only if successful. */
-int
-pfm_write_dictionary (struct file_handle *handle, struct dictionary *dict)
+struct pfm_writer *
+pfm_open_writer (struct file_handle *fh, const struct dictionary *dict)
 {
 {
-  struct pfm_fhuser_ext *ext;
-  
-  if (handle->class != NULL)
-    {
-      msg (ME, _("Cannot write file %s as portable file: already opened "
-                "for %s."),
-          handle_get_name (handle), handle->class->name);
-      return 0;
-    }
+  struct pfm_writer *w = NULL;
+  size_t i;
 
 
-  msg (VM (1), _("%s: Opening portable-file handle %s for writing."),
-       handle_get_filename (handle), handle_get_name (handle));
+  if (!fh_open (fh, "portable file", "we"))
+    goto error;
   
   /* Open the physical disk file. */
   
   /* Open the physical disk file. */
-  handle->class = &pfm_w_class;
-  handle->ext = ext = xmalloc (sizeof (struct pfm_fhuser_ext));
-  ext->file = fopen (handle_get_filename (handle), "wb");
-  ext->lc = 0;
-  if (ext->file == NULL)
+  w = xmalloc (sizeof *w);
+  w->fh = fh;
+  w->file = fopen (handle_get_filename (fh), "wb");
+  w->lc = 0;
+  w->var_cnt = 0;
+  w->vars = NULL;
+  
+  /* Check that file create succeeded. */
+  if (w->file == NULL)
     {
       msg (ME, _("An error occurred while opening \"%s\" for writing "
           "as a portable file: %s."),
     {
       msg (ME, _("An error occurred while opening \"%s\" for writing "
           "as a portable file: %s."),
-           handle_get_filename (handle), strerror (errno));
+           handle_get_filename (fh), strerror (errno));
       err_cond_fail ();
       err_cond_fail ();
-      free (ext);
-      return 0;
+      goto error;
     }
   
     }
   
-  {
-    int i;
-
-    ext->nvars = dict_get_var_cnt (dict);
-    ext->vars = xmalloc (sizeof *ext->vars * ext->nvars);
-    for (i = 0; i < ext->nvars; i++)
-      ext->vars[i] = dict_get_var (dict, i)->width;
-  }
-
-  /* Write the file header. */
-  if (!write_header (handle))
-    goto lossage;
-
-  /* Write version data. */
-  if (!write_version_data (handle))
-    goto lossage;
-
-  /* Write variables. */
-  if (!write_variables (handle, dict))
-    goto lossage;
-
-  /* Write value labels. */
-  if (!write_value_labels (handle, dict))
-    goto lossage;
+  w->var_cnt = dict_get_var_cnt (dict);
+  w->vars = xmalloc (sizeof *w->vars * w->var_cnt);
+  for (i = 0; i < w->var_cnt; i++) 
+    {
+      const struct variable *dv = dict_get_var (dict, i);
+      struct pfm_var *pv = &w->vars[i];
+      pv->width = dv->width;
+      pv->fv = dv->fv;
+    }
 
 
-  /* Write beginning of data marker. */
-  if (!bufwrite (handle, "F", 1))
-    goto lossage;
+  /* Write file header. */
+  if (!write_header (w)
+      || !write_version_data (w)
+      || !write_variables (w, dict)
+      || !write_value_labels (w, dict)
+      || !buf_write (w, "F", 1))
+    goto error;
 
 
-  msg (VM (2), _("Wrote portable-file header successfully."));
+  return w;
 
 
-  return 1;
-
-lossage:
-  msg (VM (1), _("Error writing portable-file header."));
-  fclose (ext->file);
-  free (ext->vars);
-  handle->class = NULL;
-  handle->ext = NULL;
-  return 0;
+error:
+  pfm_close_writer (w);
+  return NULL;
 }
 \f  
 /* Write NBYTES starting at BUF to the portable file represented by
    H.  Break lines properly every 80 characters.  */
 static int
 }
 \f  
 /* Write NBYTES starting at BUF to the portable file represented by
    H.  Break lines properly every 80 characters.  */
 static int
-bufwrite (struct file_handle *h, const void *buf_, size_t nbytes)
+buf_write (struct pfm_writer *w, const void *buf_, size_t nbytes)
 {
   const char *buf = buf_;
 {
   const char *buf = buf_;
-  struct pfm_fhuser_ext *ext = h->ext;
 
   assert (buf != NULL);
 
   assert (buf != NULL);
-  while (nbytes + ext->lc >= 80)
+  while (nbytes + w->lc >= 80)
     {
     {
-      size_t n = 80 - ext->lc;
+      size_t n = 80 - w->lc;
       
       
-      if (n && 1 != fwrite (buf, n, 1, ext->file))
-       goto lossage;
+      if (n && fwrite (buf, n, 1, w->file) != 1)
+       goto error;
       
       
-      /* PORTME: line ends. */
-      if (1 != fwrite ("\r\n", 2, 1, ext->file))
-       goto lossage;
+      if (fwrite ("\r\n", 2, 1, w->file) != 1)
+       goto error;
 
       nbytes -= n;
       buf += n;
 
       nbytes -= n;
       buf += n;
-      ext->lc = 0;
+      w->lc = 0;
     }
 
     }
 
-  if (nbytes && 1 != fwrite (buf, nbytes, 1, ext->file))
-    goto lossage;
-  ext->lc += nbytes;
+  if (nbytes && 1 != fwrite (buf, nbytes, 1, w->file))
+    goto error;
+  w->lc += nbytes;
   
   return 1;
 
   
   return 1;
 
- lossage:
-  abort ();
+ error:
   msg (ME, _("%s: Writing portable file: %s."),
   msg (ME, _("%s: Writing portable file: %s."),
-       handle_get_filename (h), strerror (errno));
+       handle_get_filename (w->fh), strerror (errno));
   return 0;
 }
 
 /* Write D to the portable file as a floating-point field, and return
    success. */
 static int
   return 0;
 }
 
 /* Write D to the portable file as a floating-point field, and return
    success. */
 static int
-write_float (struct file_handle *h, double d)
+write_float (struct pfm_writer *w, double d)
 {
   int neg = 0;
   char *mantissa;
 {
   int neg = 0;
   char *mantissa;
@@ -191,7 +175,7 @@ write_float (struct file_handle *h, double d)
     }
   
   if (d == fabs (SYSMIS) || d == HUGE_VAL)
     }
   
   if (d == fabs (SYSMIS) || d == HUGE_VAL)
-    return bufwrite (h, "*.", 2);
+    return buf_write (w, "*.", 2);
   
   /* Use GNU libgmp2 to convert D into base-30. */
   {
   
   /* Use GNU libgmp2 to convert D into base-30. */
   {
@@ -238,7 +222,7 @@ write_float (struct file_handle *h, double d)
     }
   *cp++ = '/';
   
     }
   *cp++ = '/';
   
-  success = bufwrite (h, buf, cp - buf);
+  success = buf_write (w, buf, cp - buf);
   local_free (buf);
   free (mantissa);
   return success;
   local_free (buf);
   free (mantissa);
   return success;
@@ -246,7 +230,7 @@ write_float (struct file_handle *h, double d)
 
 /* Write N to the portable file as an integer field, and return success. */
 static int
 
 /* Write N to the portable file as an integer field, and return success. */
 static int
-write_int (struct file_handle *h, int n)
+write_int (struct pfm_writer *w, int n)
 {
   char buf[64];
   char *bp = &buf[64];
 {
   char buf[64];
   char *bp = &buf[64];
@@ -277,27 +261,27 @@ write_int (struct file_handle *h, int n)
   if (neg)
     *--bp = '-';
 
   if (neg)
     *--bp = '-';
 
-  return bufwrite (h, bp, &buf[64] - bp);
+  return buf_write (w, bp, &buf[64] - bp);
 }
 
 /* Write S to the portable file as a string field. */
 static int
 }
 
 /* Write S to the portable file as a string field. */
 static int
-write_string (struct file_handle *h, const char *s)
+write_string (struct pfm_writer *w, const char *s)
 {
   size_t n = strlen (s);
 {
   size_t n = strlen (s);
-  return write_int (h, (int) n) && bufwrite (h, s, n);
+  return write_int (w, (int) n) && buf_write (w, s, n);
 }
 \f
 /* Write file header. */
 static int
 }
 \f
 /* Write file header. */
 static int
-write_header (struct file_handle *h)
+write_header (struct pfm_writer *w)
 {
   /* PORTME. */
   {
     int i;
 
     for (i = 0; i < 5; i++)
 {
   /* PORTME. */
   {
     int i;
 
     for (i = 0; i < 5; i++)
-      if (!bufwrite (h, "ASCII SPSS PORT FILE                    ", 40))
+      if (!buf_write (w, "ASCII SPSS PORT FILE                    ", 40))
        return 0;
   }
   
        return 0;
   }
   
@@ -312,11 +296,11 @@ write_header (struct file_handle *h)
        "0000000000000000000000000000000000000000000000000000000000000000"
       };
 
        "0000000000000000000000000000000000000000000000000000000000000000"
       };
 
-    if (!bufwrite (h, spss2ascii, 256))
+    if (!buf_write (w, spss2ascii, 256))
       return 0;
   }
 
       return 0;
   }
 
-  if (!bufwrite (h, "SPSSPORT", 8))
+  if (!buf_write (w, "SPSSPORT", 8))
     return 0;
 
   return 1;
     return 0;
 
   return 1;
@@ -324,9 +308,9 @@ write_header (struct file_handle *h)
 
 /* Writes version, date, and identification records. */
 static int
 
 /* Writes version, date, and identification records. */
 static int
-write_version_data (struct file_handle *h)
+write_version_data (struct pfm_writer *w)
 {
 {
-  if (!bufwrite (h, "A", 1))
+  if (!buf_write (w, "A", 1))
     return 0;
   
   {
     return 0;
   
   {
@@ -348,16 +332,16 @@ write_version_data (struct file_handle *h)
     sprintf (date_str, "%04d%02d%02d",
             tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday);
     sprintf (time_str, "%02d%02d%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
     sprintf (date_str, "%04d%02d%02d",
             tmp->tm_year + 1900, tmp->tm_mon + 1, tmp->tm_mday);
     sprintf (time_str, "%02d%02d%02d", tmp->tm_hour, tmp->tm_min, tmp->tm_sec);
-    if (!write_string (h, date_str) || !write_string (h, time_str))
+    if (!write_string (w, date_str) || !write_string (w, time_str))
       return 0;
   }
 
   /* Product identification. */
       return 0;
   }
 
   /* Product identification. */
-  if (!bufwrite (h, "1", 1) || !write_string (h, version))
+  if (!buf_write (w, "1", 1) || !write_string (w, version))
     return 0;
 
   /* Subproduct identification. */
     return 0;
 
   /* Subproduct identification. */
-  if (!bufwrite (h, "3", 1) || !write_string (h, host_system))
+  if (!buf_write (w, "3", 1) || !write_string (w, host_system))
     return 0;
 
   return 1;
     return 0;
 
   return 1;
@@ -365,31 +349,31 @@ write_version_data (struct file_handle *h)
 
 /* Write format F to file H, and return success. */
 static int
 
 /* Write format F to file H, and return success. */
 static int
-write_format (struct file_handle *h, struct fmt_spec *f)
+write_format (struct pfm_writer *w, struct fmt_spec *f)
 {
 {
-  return (write_int (h, formats[f->type].spss)
-         && write_int (h, f->w)
-         && write_int (h, f->d));
+  return (write_int (w, formats[f->type].spss)
+         && write_int (w, f->w)
+         && write_int (w, f->d));
 }
 
 /* Write value V for variable VV to file H, and return success. */
 static int
 }
 
 /* Write value V for variable VV to file H, and return success. */
 static int
-write_value (struct file_handle *h, union value *v, struct variable *vv)
+write_value (struct pfm_writer *w, union value *v, struct variable *vv)
 {
   if (vv->type == NUMERIC)
 {
   if (vv->type == NUMERIC)
-    return write_float (h, v->f);
+    return write_float (w, v->f);
   else
   else
-    return write_int (h, vv->width) && bufwrite (h, v->s, vv->width);
+    return write_int (w, vv->width) && buf_write (w, v->s, vv->width);
 }
 
 /* Write variable records, and return success. */
 static int
 }
 
 /* Write variable records, and return success. */
 static int
-write_variables (struct file_handle *h, struct dictionary *dict)
+write_variables (struct pfm_writer *w, const struct dictionary *dict)
 {
   int i;
   
 {
   int i;
   
-  if (!bufwrite (h, "4", 1) || !write_int (h, dict_get_var_cnt (dict))
-      || !write_int (h, 161))
+  if (!buf_write (w, "4", 1) || !write_int (w, dict_get_var_cnt (dict))
+      || !write_int (w, 161))
     return 0;
 
   for (i = 0; i < dict_get_var_cnt (dict); i++)
     return 0;
 
   for (i = 0; i < dict_get_var_cnt (dict); i++)
@@ -404,17 +388,17 @@ write_variables (struct file_handle *h, struct dictionary *dict)
 
       struct variable *v = dict_get_var (dict, i);
       
 
       struct variable *v = dict_get_var (dict, i);
       
-      if (!bufwrite (h, "7", 1) || !write_int (h, v->width)
-         || !write_string (h, v->name)
-         || !write_format (h, &v->print) || !write_format (h, &v->write))
+      if (!buf_write (w, "7", 1) || !write_int (w, v->width)
+         || !write_string (w, v->name)
+         || !write_format (w, &v->print) || !write_format (w, &v->write))
        return 0;
 
       for (m = miss_types[v->miss_type], j = 0; j < (int) strlen (m); j++)
        return 0;
 
       for (m = miss_types[v->miss_type], j = 0; j < (int) strlen (m); j++)
-       if ((m[j] != ' ' && !bufwrite (h, &m[j], 1))
-           || !write_value (h, &v->missing[j], v))
+       if ((m[j] != ' ' && !buf_write (w, &m[j], 1))
+           || !write_value (w, &v->missing[j], v))
          return 0;
 
          return 0;
 
-      if (v->label && (!bufwrite (h, "C", 1) || !write_string (h, v->label)))
+      if (v->label && (!buf_write (w, "C", 1) || !write_string (w, v->label)))
        return 0;
     }
 
        return 0;
     }
 
@@ -423,7 +407,7 @@ write_variables (struct file_handle *h, struct dictionary *dict)
 
 /* Write value labels to disk.  FIXME: Inefficient. */
 static int
 
 /* Write value labels to disk.  FIXME: Inefficient. */
 static int
-write_value_labels (struct file_handle *h, struct dictionary *dict)
+write_value_labels (struct pfm_writer *w, const struct dictionary *dict)
 {
   int i;
 
 {
   int i;
 
@@ -436,16 +420,16 @@ write_value_labels (struct file_handle *h, struct dictionary *dict)
       if (!val_labs_count (v->val_labs))
        continue;
 
       if (!val_labs_count (v->val_labs))
        continue;
 
-      if (!bufwrite (h, "D", 1)
-         || !write_int (h, 1)
-         || !write_string (h, v->name)
-         || !write_int (h, val_labs_count (v->val_labs)))
+      if (!buf_write (w, "D", 1)
+         || !write_int (w, 1)
+         || !write_string (w, v->name)
+         || !write_int (w, val_labs_count (v->val_labs)))
        return 0;
 
       for (vl = val_labs_first_sorted (v->val_labs, &j); vl != NULL;
            vl = val_labs_next (v->val_labs, &j)) 
        return 0;
 
       for (vl = val_labs_first_sorted (v->val_labs, &j); vl != NULL;
            vl = val_labs_next (v->val_labs, &j)) 
-       if (!write_value (h, &vl->value, v)
-           || !write_string (h, vl->label)) 
+       if (!write_value (w, &vl->value, v)
+           || !write_string (w, vl->label)) 
           {
             val_labs_done (&j);
             return 0; 
           {
             val_labs_done (&j);
             return 0; 
@@ -458,24 +442,23 @@ write_value_labels (struct file_handle *h, struct dictionary *dict)
 /* Writes case ELEM to the portable file represented by H.  Returns
    success. */
 int 
 /* Writes case ELEM to the portable file represented by H.  Returns
    success. */
 int 
-pfm_write_case (struct file_handle *h, const union value *elem)
+pfm_write_case (struct pfm_writer *w, struct ccase *c)
 {
 {
-  struct pfm_fhuser_ext *ext = h->ext;
-  
   int i;
   
   int i;
   
-  for (i = 0; i < ext->nvars; i++)
+  for (i = 0; i < w->var_cnt; i++)
     {
     {
-      const int width = ext->vars[i];
+      struct pfm_var *v = &w->vars[i];
       
       
-      if (width == 0)
+      if (v->width == 0)
        {
        {
-         if (!write_float (h, elem[i].f))
+         if (!write_float (w, case_num (c, v->fv)))
            return 0;
        }
       else
        {
            return 0;
        }
       else
        {
-         if (!write_int (h, width) || !bufwrite (h, elem[i].c, width))
+         if (!write_int (w, v->width)
+              || !buf_write (w, case_str (c, v->fv), v->width))
            return 0;
        }
     }
            return 0;
        }
     }
@@ -484,33 +467,30 @@ pfm_write_case (struct file_handle *h, const union value *elem)
 }
 
 /* Closes a portable file after we're done with it. */
 }
 
 /* Closes a portable file after we're done with it. */
-static void
-pfm_close (struct file_handle *h)
+void
+pfm_close_writer (struct pfm_writer *w)
 {
 {
-  struct pfm_fhuser_ext *ext = h->ext;
+  if (w == NULL)
+    return;
+
+  fh_close (w->fh, "portable file", "we");
   
   
-  {
-    char buf[80];
+  if (w->file != NULL)
+    {
+      char buf[80];
     
     
-    int n = 80 - ext->lc;
-    if (n == 0)
-      n = 80;
+      int n = 80 - w->lc;
+      if (n == 0)
+        n = 80;
 
 
-    memset (buf, 'Z', n);
-    bufwrite (h, buf, n);
-  }
+      memset (buf, 'Z', n);
+      buf_write (w, buf, n);
 
 
-  if (EOF == fclose (ext->file))
-    msg (ME, _("%s: Closing portable file: %s."),
-         handle_get_filename (h), strerror (errno));
+      if (fclose (w->file) == EOF)
+        msg (ME, _("%s: Closing portable file: %s."),
+             handle_get_filename (w->fh), strerror (errno));
+    }
 
 
-  free (ext->vars);
-  free (ext);
+  free (w->vars);
+  free (w);
 }
 }
-
-static struct fh_ext_class pfm_w_class =
-{
-  6,
-  N_("writing as a portable file"),
-  pfm_close,
-};
diff --git a/src/pfm-write.h b/src/pfm-write.h
new file mode 100644 (file)
index 0000000..1f845d8
--- /dev/null
@@ -0,0 +1,33 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 1997-9, 2000 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., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#ifndef PFM_WRITE_H
+#define PFM_WRITE_H
+
+/* Portable file writing. */
+
+struct file_handle;
+struct dictionary;
+struct ccase;
+struct pfm_writer *pfm_open_writer (struct file_handle *,
+                                    const struct dictionary *);
+int pfm_write_case (struct pfm_writer *, struct ccase *);
+void pfm_close_writer (struct pfm_writer *);
+
+#endif /* pfm-write.h */
diff --git a/src/pfm.h b/src/pfm.h
deleted file mode 100644 (file)
index 172a8f5..0000000
--- a/src/pfm.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 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., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
-
-#if !pfm_h
-#define pfm_h 1
-
-/* Portable file manager (pfm).
-
-   This module is in charge of reading and writing portable files.
-   pfm is an fhuser, so see file-handle.h for the fhuser interface.  */
-
-/* Portable file types. */
-enum
-  {
-    PFM_COMM,
-    PFM_TAPE
-  };
-
-/* Information produced by pfm_read_dictionary() that doesn't fit into
-   a dictionary struct. */
-struct pfm_read_info
-  {
-    char creation_date[11];    /* `dd mm yyyy' plus a null. */
-    char creation_time[9];     /* `hh:mm:ss' plus a null. */
-    char product[61];          /* Product name plus a null. */
-    char subproduct[61];       /* Subproduct name plus a null. */
-  };
-
-struct dictionary;
-struct file_handle;
-struct ccase;
-union value;
-
-struct dictionary *pfm_read_dictionary (struct file_handle *,
-                                       struct pfm_read_info *);
-int pfm_read_case (struct file_handle *, struct ccase *, struct dictionary *);
-
-int pfm_write_dictionary (struct file_handle *, struct dictionary *);
-int pfm_write_case (struct file_handle *, const union value *elem);
-
-#endif /* !pfm_h */
index 29e0c791685ade24fc4cf4596a689544b56fe9f9..4e0725e6d64acd93677228c42a3975d3eb33a0cf 100644 (file)
@@ -19,16 +19,20 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 02111-1307, USA. */
 
 
 02111-1307, USA. */
 
 
+#include <config.h>
 #include "chart.h"
 #include <float.h>
 #include <assert.h>
 #include <math.h>
 #include <stdio.h>
 #include "chart.h"
 #include <float.h>
 #include <assert.h>
 #include <math.h>
 #include <stdio.h>
+#include "str.h"
 #include "value-labels.h"
 
 
 /* Pie charts of course need to know Pi :) */
 #include "value-labels.h"
 
 
 /* Pie charts of course need to know Pi :) */
+#ifndef M_PI
 #define M_PI ( 22.0 / 7.0 ) 
 #define M_PI ( 22.0 / 7.0 ) 
+#endif
 
 
 #define min(A,B) ((A>B)?B:A)
 
 
 #define min(A,B) ((A>B)?B:A)
@@ -45,12 +49,11 @@ draw_segment(struct chart *ch,
 
 /* Draw a pie chart */
 void
 
 /* Draw a pie chart */
 void
-draw_piechart(struct chart *ch, const struct variable *var)
+draw_piechart(struct chart *ch, const struct variable *var,
+              const struct freq_tab *frq_tab)
 {
   int i;
 
 {
   int i;
 
-  const struct freq_tab *frq_tab = &var->p.frq.tab ;
-
   const int n_data = frq_tab->n_valid;
   const double left_label = ch->data_left + 
     (ch->data_right - ch->data_left)/10.0;
   const int n_data = frq_tab->n_valid;
   const double left_label = ch->data_left + 
     (ch->data_right - ch->data_left)/10.0;
index 1bbc5ae6f8df07af94ed6cc0cfd94cd9b07ee7f5..b2ef755cf11bc3fde5f53b158d0e65e0f49604f2 100644 (file)
@@ -25,7 +25,7 @@
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
-#include "dfm.h"
+#include "dfm-write.h"
 #include "error.h"
 #include "expr.h"
 #include "file-handle.h"
 #include "error.h"
 #include "expr.h"
 #include "file-handle.h"
@@ -70,14 +70,15 @@ enum
     PRT_CMD_MASK = 1,          /* Command type mask. */
     PRT_PRINT = 0,             /* PRINT transformation identifier. */
     PRT_WRITE = 1,             /* WRITE transformation identifier. */
     PRT_CMD_MASK = 1,          /* Command type mask. */
     PRT_PRINT = 0,             /* PRINT transformation identifier. */
     PRT_WRITE = 1,             /* WRITE transformation identifier. */
-    PRT_EJECT = 002            /* Can be combined with CMD_PRINT only. */
+    PRT_EJECT = 002,           /* Can be combined with CMD_PRINT only. */
+    PRT_BINARY = 004            /* File is binary, omit newlines. */
   };
 
 /* PRINT, PRINT EJECT, WRITE private data structure. */
 struct print_trns
   {
     struct trns_header h;
   };
 
 /* PRINT, PRINT EJECT, WRITE private data structure. */
 struct print_trns
   {
     struct trns_header h;
-    struct file_handle *handle;        /* Output file, NULL=listing file. */
+    struct dfm_writer *writer; /* Output file, NULL=listing file. */
     int options;               /* PRT_* bitmapped field. */
     struct prt_out_spec *spec; /* Output specifications. */
     int max_width;             /* Maximum line width including null. */
     int options;               /* PRT_* bitmapped field. */
     struct prt_out_spec *spec; /* Output specifications. */
     int max_width;             /* Maximum line width including null. */
@@ -100,8 +101,8 @@ static int internal_cmd_print (int flags);
 static trns_proc_func print_trns_proc;
 static trns_free_func print_trns_free;
 static int parse_specs (void);
 static trns_proc_func print_trns_proc;
 static trns_free_func print_trns_free;
 static int parse_specs (void);
-static void dump_table (void);
-static void append_var_spec (struct prt_out_spec *spec);
+static void dump_table (const struct file_handle *);
+static void append_var_spec (struct prt_out_spec *);
 static void alloc_line (void);
 \f
 /* Basic parsing. */
 static void alloc_line (void);
 \f
 /* Basic parsing. */
@@ -132,16 +133,14 @@ cmd_write (void)
 static int
 internal_cmd_print (int f)
 {
 static int
 internal_cmd_print (int f)
 {
-  /* 0=print no table, 1=print table.  (TABLE subcommand.)  */
-  int table = 0;
-
-  /* malloc()'d transformation. */
-  struct print_trns *trns;
+  int table = 0;                /* Print table? */
+  struct print_trns *trns;      /* malloc()'d transformation. */
+  struct file_handle *fh = NULL;
 
   /* Fill in prt to facilitate error-handling. */
   prt.h.proc = print_trns_proc;
   prt.h.free = print_trns_free;
 
   /* Fill in prt to facilitate error-handling. */
   prt.h.proc = print_trns_proc;
   prt.h.free = print_trns_free;
-  prt.handle = NULL;
+  prt.writer = NULL;
   prt.options = f;
   prt.spec = NULL;
   prt.line = NULL;
   prt.options = f;
   prt.spec = NULL;
   prt.line = NULL;
@@ -157,16 +156,16 @@ internal_cmd_print (int f)
        {
          lex_match ('=');
 
        {
          lex_match ('=');
 
-         prt.handle = fh_parse_file_handle ();
-         if (!prt.handle)
-           goto lossage;
+         fh = fh_parse ();
+         if (fh == NULL)
+           goto error;
        }
       else if (lex_match_id ("RECORDS"))
        {
          lex_match ('=');
          lex_match ('(');
          if (!lex_force_int ())
        }
       else if (lex_match_id ("RECORDS"))
        {
          lex_match ('=');
          lex_match ('(');
          if (!lex_force_int ())
-           goto lossage;
+           goto error;
          nrec = lex_integer ();
          lex_get ();
          lex_match (')');
          nrec = lex_integer ();
          lex_get ();
          lex_match (')');
@@ -178,20 +177,27 @@ internal_cmd_print (int f)
       else
        {
          lex_error (_("expecting a valid subcommand"));
       else
        {
          lex_error (_("expecting a valid subcommand"));
-         goto lossage;
+         goto error;
        }
     }
 
   /* Parse variables and strings. */
   if (!parse_specs ())
        }
     }
 
   /* Parse variables and strings. */
   if (!parse_specs ())
-    goto lossage;
-  
-  if (prt.handle != NULL && !dfm_open_for_writing (prt.handle))
-    goto lossage;
+    goto error;
+
+  if (fh != NULL)
+    {
+      prt.writer = dfm_open_writer (fh);
+      if (prt.writer == NULL)
+        goto error;
+
+      if (handle_get_mode (fh) == MODE_BINARY)
+        prt.options |= PRT_BINARY;
+    }
 
   /* Output the variable table if requested. */
   if (table)
 
   /* Output the variable table if requested. */
   if (table)
-    dump_table ();
+    dump_table (fh);
 
   /* Count the maximum line width.  Allocate linebuffer if
      applicable. */
 
   /* Count the maximum line width.  Allocate linebuffer if
      applicable. */
@@ -204,7 +210,7 @@ internal_cmd_print (int f)
 
   return CMD_SUCCESS;
 
 
   return CMD_SUCCESS;
 
lossage:
error:
   print_trns_free ((struct trns_header *) & prt);
   return CMD_FAILURE;
 }
   print_trns_free ((struct trns_header *) & prt);
   return CMD_FAILURE;
 }
@@ -775,7 +781,7 @@ fail:
 /* Prints the table produced by the TABLE subcommand to the listing
    file. */
 static void
 /* Prints the table produced by the TABLE subcommand to the listing
    file. */
 static void
-dump_table (void)
+dump_table (const struct file_handle *fh)
 {
   struct prt_out_spec *spec;
   struct tab_table *t;
 {
   struct prt_out_spec *spec;
   struct tab_table *t;
@@ -831,9 +837,9 @@ dump_table (void)
        assert (0);
       }
 
        assert (0);
       }
 
-  if (prt.handle != NULL)
+  if (fh != NULL)
     tab_title (t, 1, _("Writing %d record(s) to file %s."),
     tab_title (t, 1, _("Writing %d record(s) to file %s."),
-               recno, handle_get_filename (prt.handle));
+               recno, handle_get_filename (fh));
   else
     tab_title (t, 1, _("Writing %d record(s) to the listing file."), recno);
   tab_submit (t);
   else
     tab_title (t, 1, _("Writing %d record(s) to the listing file."), recno);
   tab_submit (t);
@@ -911,15 +917,14 @@ print_trns_proc (struct trns_header * trns, struct ccase * c,
   if (t->options & PRT_EJECT)
     som_eject_page ();
 
   if (t->options & PRT_EJECT)
     som_eject_page ();
 
-  /* Note that a field written to a place where a field has already
-     been written truncates the record.  `PRINT /A B (T10,F8,T1,F8).'
-     only outputs B.  This is an example of bug-for-bug compatibility,
-     in the author's opinion. */
+  /* Note that a field written to a place where a field has
+     already been written truncates the record.  `PRINT /A B
+     (T10,F8,T1,F8).' only outputs B.  */
   for (i = t->spec; i; i = i->next)
     switch (i->type)
       {
       case PRT_NEWLINE:
   for (i = t->spec; i; i = i->next)
     switch (i->type)
       {
       case PRT_NEWLINE:
-       if (t->handle == NULL)
+       if (t->writer == NULL)
          {
            buf[len] = 0;
            tab_output_text (TAT_FIX | TAT_NOWRAP, buf);
          {
            buf[len] = 0;
            tab_output_text (TAT_FIX | TAT_NOWRAP, buf);
@@ -927,7 +932,7 @@ print_trns_proc (struct trns_header * trns, struct ccase * c,
        else
          {
            if ((t->options & PRT_CMD_MASK) == PRT_PRINT
        else
          {
            if ((t->options & PRT_CMD_MASK) == PRT_PRINT
-               || handle_get_mode (t->handle) != MODE_BINARY)
+                || !(t->options & PRT_BINARY))
              {
                /* PORTME: Line ends. */
 #ifdef __MSDOS__
              {
                /* PORTME: Line ends. */
 #ifdef __MSDOS__
@@ -936,7 +941,7 @@ print_trns_proc (struct trns_header * trns, struct ccase * c,
                buf[len++] = '\n';
              }
 
                buf[len++] = '\n';
              }
 
-           dfm_put_record (t->handle, buf, len);
+           dfm_put_record (t->writer, buf, len);
          }
 
        memset (buf, ' ', t->max_width);
          }
 
        memset (buf, ' ', t->max_width);
@@ -1003,7 +1008,7 @@ struct print_space_trns
 {
   struct trns_header h;
 
 {
   struct trns_header h;
 
-  struct file_handle *handle;  /* Output file, NULL=listing file. */
+  struct dfm_writer *writer;    /* Output data file. */
   struct expression *e;                /* Number of lines; NULL=1. */
 }
 print_space_trns;
   struct expression *e;                /* Number of lines; NULL=1. */
 }
 print_space_trns;
@@ -1015,20 +1020,21 @@ int
 cmd_print_space (void)
 {
   struct print_space_trns *t;
 cmd_print_space (void)
 {
   struct print_space_trns *t;
-  struct file_handle *handle;
+  struct file_handle *fh;
   struct expression *e;
   struct expression *e;
+  struct dfm_writer *writer;
 
   if (lex_match_id ("OUTFILE"))
     {
       lex_match ('=');
 
 
   if (lex_match_id ("OUTFILE"))
     {
       lex_match ('=');
 
-      handle = fh_parse_file_handle ();
-      if (handle == NULL)
+      fh = fh_parse ();
+      if (fh == NULL)
        return CMD_FAILURE;
       lex_get ();
     }
   else
        return CMD_FAILURE;
       lex_get ();
     }
   else
-    handle = NULL;
+    fh = NULL;
 
   if (token != '.')
     {
 
   if (token != '.')
     {
@@ -1043,19 +1049,25 @@ cmd_print_space (void)
   else
     e = NULL;
 
   else
     e = NULL;
 
-  if (handle != NULL && !dfm_open_for_writing (handle))
+  if (fh != NULL)
     {
     {
-      expr_free (e);
-      return CMD_FAILURE;
+      writer = dfm_open_writer (fh);
+      if (writer == NULL) 
+        {
+          expr_free (e);
+          return CMD_FAILURE;
+        } 
     }
     }
-
+  else
+    writer = NULL;
+  
   t = xmalloc (sizeof *t);
   t->h.proc = print_space_trns_proc;
   if (e)
     t->h.free = print_space_trns_free;
   else
     t->h.free = NULL;
   t = xmalloc (sizeof *t);
   t->h.proc = print_space_trns_proc;
   if (e)
     t->h.free = print_space_trns_free;
   else
     t->h.free = NULL;
-  t->handle = handle;
+  t->writer = writer;
   t->e = e;
 
   add_transformation ((struct trns_header *) t);
   t->e = e;
 
   add_transformation ((struct trns_header *) t);
@@ -1087,7 +1099,7 @@ print_space_trns_proc (struct trns_header * trns, struct ccase * c,
   else
     n = 1;
 
   else
     n = 1;
 
-  if (t->handle == NULL)
+  if (t->writer == NULL)
     while (n--)
       som_blank_line ();
   else
     while (n--)
       som_blank_line ();
   else
@@ -1102,7 +1114,7 @@ print_space_trns_proc (struct trns_header * trns, struct ccase * c,
       buf[0] = '\n';
 #endif
       while (n--)
       buf[0] = '\n';
 #endif
       while (n--)
-       dfm_put_record (t->handle, buf, LINE_END_WIDTH);
+       dfm_put_record (t->writer, buf, LINE_END_WIDTH);
     }
 
   return -1;
     }
 
   return -1;
index fec3e3ab4b0d21289d573c46c4f2fb9ba0cd4d93..0ffc79f0ea10d4b2356d6f8ce2cc6d3eb6bf9077 100644 (file)
--- a/src/q2c.c
+++ b/src/q2c.c
@@ -2022,11 +2022,6 @@ dump_header (void)
   dump (0, "   Generated by q2c from %s on %s.", ifn, timep);
   dump (0, "   Do not modify!");
   dump (0, " */");
   dump (0, "   Generated by q2c from %s on %s.", ifn, timep);
   dump (0, "   Do not modify!");
   dump (0, " */");
-
-  dump (0, nullstr);
-  dump (0, "#include \"settings.h\"");
-  dump (0, "#include \"subclist.h\"");
-  dump (0, nullstr);
 }
 
 /* Write out commands to free variable state. */
 }
 
 /* Write out commands to free variable state. */
@@ -2165,7 +2160,9 @@ main (int argc, char *argv[])
          dump (0, "#include \"alloc.h\"");
          dump (0, "#include \"error.h\"");
          dump (0, "#include \"lexer.h\"");
          dump (0, "#include \"alloc.h\"");
          dump (0, "#include \"error.h\"");
          dump (0, "#include \"lexer.h\"");
+          dump (0, "#include \"settings.h\"");
          dump (0, "#include \"str.h\"");
          dump (0, "#include \"str.h\"");
+          dump (0, "#include \"subclist.h\"");
          dump (0, "#include \"var.h\"");
 
          dump (0, nullstr);
          dump (0, "#include \"var.h\"");
 
          dump (0, nullstr);
index 089caba12fcb02353c1ecd920b03525335bacf9b..034ae959e83c2bd7aa02e5c2bbfd26b3fd954aa4 100644 (file)
@@ -25,6 +25,7 @@
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "lexer.h"
 #include "magic.h"
 #include "error.h"
 #include "lexer.h"
 #include "magic.h"
index 70243468b743888633f82e14d974c71e53d8820b..0219f28345a0c99d017add9407c8764281686ed0 100644 (file)
@@ -22,6 +22,7 @@
 #include "error.h"
 #include "alloc.h"
 #include "command.h"
 #include "error.h"
 #include "alloc.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "hash.h"
 #include "lexer.h"
 #include "error.h"
 #include "hash.h"
 #include "lexer.h"
index 2d788e27a273960e6c1dc7d19d8e0b289812ef67..3b57b66a7398db1b391e5cadc38eb8daa1e76faf 100644 (file)
@@ -25,6 +25,7 @@
 #include <stdlib.h>
 #include "alloc.h"
 #include "command.h"
 #include <stdlib.h>
 #include "alloc.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "getline.h"
 #include "lexer.h"
 #include "error.h"
 #include "getline.h"
 #include "lexer.h"
index 6f6f2d0279269083acb9e072e6648d9c9bf73285..8d224d0215d793e03d2966d5319efece3eb2c208 100644 (file)
@@ -20,6 +20,7 @@
 #include <config.h>
 #include "alloc.h"
 #include "command.h"
 #include <config.h>
 #include "alloc.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "expr.h"
 #include "lexer.h"
 #include "error.h"
 #include "expr.h"
 #include "lexer.h"
index 8ac6f8bddc2061d0b60a9f5330587f75ce8fa8ae..9c0cc6d3115b1b37cc411471856e3f0ac35be844 100644 (file)
    02111-1307, USA. */
 
 #include <config.h>
    02111-1307, USA. */
 
 #include <config.h>
-#include "sfm.h"
+#include "sfm-read.h"
 #include "sfmP.h"
 #include "error.h"
 #include <stdlib.h>
 #include <ctype.h>
 #include <errno.h>
 #include <float.h>
 #include "sfmP.h"
 #include "error.h"
 #include <stdlib.h>
 #include <ctype.h>
 #include <errno.h>
 #include <float.h>
+#include <setjmp.h>
 #include "alloc.h"
 #include "case.h"
 #include "alloc.h"
 #include "case.h"
+#include "dictionary.h"
 #include "error.h"
 #include "file-handle.h"
 #include "filename.h"
 #include "error.h"
 #include "file-handle.h"
 #include "filename.h"
 
 #include "debug-print.h"
 
 
 #include "debug-print.h"
 
-/* PORTME: This file may require substantial revision for those
-   systems that don't meet the typical 32-bit integer/64-bit double
-   model.  It's kinda hard to tell without having one of them on my
-   desk.  */
-
-/* sfm's file_handle extension. */
-struct sfm_fhuser_ext
+/* System file reader. */
+struct sfm_reader
   {
   {
-    FILE *file;                        /* Actual file. */
-    int opened;                        /* Reference count. */
-
-    struct dictionary *dict;   /* File's dictionary. */
+    struct file_handle *fh;     /* File handle. */
+    FILE *file;                        /* File stream. */
 
     int reverse_endian;                /* 1=file has endianness opposite us. */
 
     int reverse_endian;                /* 1=file has endianness opposite us. */
-    int case_size;             /* Number of `values's per case. */
-    long ncases;               /* Number of cases, -1 if unknown. */
+    int fix_specials;           /* 1=SYSMIS/HIGHEST/LOWEST differs from us. */
+    int value_cnt;             /* Number of `union values's per case. */
+    long case_cnt;             /* Number of cases, -1 if unknown. */
     int compressed;            /* 1=compressed, 0=not compressed. */
     double bias;               /* Compression bias, usually 100.0. */
     int compressed;            /* 1=compressed, 0=not compressed. */
     double bias;               /* Compression bias, usually 100.0. */
-    int weight_index;          /* 0-based index of weighting variable, or -1. */
+    int weight_idx;            /* 0-based index of weighting variable, or -1. */
+
+    /* Variables. */
+    struct sfm_var *vars;       /* Variables. */
+    size_t var_cnt;             /* Number of variables. */
 
     /* File's special constants. */
     flt64 sysmis;
     flt64 highest;
     flt64 lowest;
 
 
     /* File's special constants. */
     flt64 sysmis;
     flt64 highest;
     flt64 lowest;
 
-    /* Uncompression buffer. */
+    /* Decompression buffer. */
     flt64 *buf;                        /* Buffer data. */
     flt64 *ptr;                        /* Current location in buffer. */
     flt64 *end;                        /* End of buffer data. */
 
     /* Compression instruction octet. */
     flt64 *buf;                        /* Buffer data. */
     flt64 *ptr;                        /* Current location in buffer. */
     flt64 *end;                        /* End of buffer data. */
 
     /* Compression instruction octet. */
-    unsigned char x[sizeof (flt64)];
-    /* Current instruction octet. */
+    unsigned char x[8];         /* Current instruction octet. */
     unsigned char *y;          /* Location in current instruction octet. */
   };
 
     unsigned char *y;          /* Location in current instruction octet. */
   };
 
-static struct fh_ext_class sfm_r_class;
+/* A variable in a system file. */
+struct sfm_var 
+  {
+    int width;                  /* 0=numeric, otherwise string width. */
+    int fv;                     /* Index into case. */
+  };
 \f
 /* Utilities. */
 
 \f
 /* Utilities. */
 
-/* bswap_int32(): Reverse the byte order of 32-bit integer *X. */
+/* Swap bytes *A and *B. */
 static inline void
 static inline void
-bswap_int32 (int32 *x)
+bswap (unsigned char *a, unsigned char *b) 
 {
 {
-  unsigned char *y = (unsigned char *) x;
-  unsigned char t;
-
-  t = y[0];
-  y[0] = y[3];
-  y[3] = t;
+  unsigned char t = *a;
+  *a = *b;
+  *b = t;
+}
 
 
-  t = y[1];
-  y[1] = y[2];
-  y[2] = t;
+/* bswap_int32(): Reverse the byte order of 32-bit integer *X. */
+static inline void
+bswap_int32 (int32 *x_)
+{
+  unsigned char *x = (unsigned char *) x_;
+  bswap (x + 0, x + 3);
+  bswap (x + 1, x + 2);
 }
 
 /* Reverse the byte order of 64-bit floating point *X. */
 static inline void
 }
 
 /* Reverse the byte order of 64-bit floating point *X. */
 static inline void
-bswap_flt64 (flt64 *x)
+bswap_flt64 (flt64 *x_)
 {
 {
-  unsigned char *y = (unsigned char *) x;
-  unsigned char t;
-
-  t = y[0];
-  y[0] = y[7];
-  y[7] = t;
-
-  t = y[1];
-  y[1] = y[6];
-  y[6] = t;
-
-  t = y[2];
-  y[2] = y[5];
-  y[5] = t;
-
-  t = y[3];
-  y[3] = y[4];
-  y[4] = t;
+  unsigned char *x = (unsigned char *) x_;
+  bswap (x + 0, x + 7);
+  bswap (x + 1, x + 6);
+  bswap (x + 2, x + 5);
+  bswap (x + 3, x + 4);
 }
 
 static void
 }
 
 static void
@@ -152,172 +145,152 @@ corrupt_msg (int class, const char *format,...)
 }
 
 /* Closes a system file after we're done with it. */
 }
 
 /* Closes a system file after we're done with it. */
-static void
-sfm_close (struct file_handle *h)
-{
-  struct sfm_fhuser_ext *ext = h->ext;
-
-  ext->opened--;
-  assert (ext->opened == 0);
-  if (EOF == fn_close (handle_get_filename (h), ext->file))
-    msg (ME, _("%s: Closing system file: %s."),
-         handle_get_filename (h), strerror (errno));
-  free (ext->buf);
-  free (h->ext);
-}
-
-/* Closes a system file if we're done with it. */
 void
 void
-sfm_maybe_close (struct file_handle *h)
+sfm_close_reader (struct sfm_reader *r)
 {
 {
-  struct sfm_fhuser_ext *ext = h->ext;
+  if (r == NULL)
+    return;
 
 
-  if (ext->opened == 1)
-    fh_close_handle (h);
-  else
-    ext->opened--;
+  if (r->fh != NULL)
+    fh_close (r->fh, "system file", "rs");
+  if (fn_close (handle_get_filename (r->fh), r->file) == EOF)
+    msg (ME, _("%s: Closing system file: %s."),
+         handle_get_filename (r->fh), strerror (errno));
+  free (r->vars);
+  free (r->buf);
+  free (r);
 }
 \f
 /* Dictionary reader. */
 
 }
 \f
 /* Dictionary reader. */
 
-static void *bufread (struct file_handle *handle, void *buf, size_t nbytes,
-                     size_t minalloc);
+static void *buf_read (struct sfm_reader *, void *buf, size_t byte_cnt,
+                       size_t min_alloc);
+
+static int read_header (struct sfm_reader *,
+                        struct dictionary *, struct sfm_read_info *);
+static int parse_format_spec (struct sfm_reader *, int32,
+                             struct fmt_spec *, struct variable *);
+static int read_value_labels (struct sfm_reader *, struct dictionary *,
+                              struct variable **var_by_idx);
+static int read_variables (struct sfm_reader *,
+                           struct dictionary *, struct variable ***var_by_idx);
+static int read_machine_int32_info (struct sfm_reader *, int size, int count);
+static int read_machine_flt64_info (struct sfm_reader *, int size, int count);
+static int read_documents (struct sfm_reader *, struct dictionary *);
 
 
-static int read_header (struct file_handle *h, struct sfm_read_info *inf);
-static int parse_format_spec (struct file_handle *h, int32 s,
-                             struct fmt_spec *v, struct variable *vv);
-static int read_value_labels (struct file_handle *h, struct variable **var_by_index);
-static int read_variables (struct file_handle *h, struct variable ***var_by_index);
-static int read_machine_int32_info (struct file_handle *h, int size, int count);
-static int read_machine_flt64_info (struct file_handle *h, int size, int count);
-static int read_documents (struct file_handle *h);
+static int fread_ok (struct sfm_reader *, void *, size_t);
 
 
-/* Displays the message X with corrupt_msg, then jumps to the lossage
+/* Displays the message X with corrupt_msg, then jumps to the error
    label. */
    label. */
-#define lose(X)                                        \
-       do                                      \
-         {                                     \
-           corrupt_msg X;                      \
-           goto lossage;                       \
-         }                                     \
-       while (0)
-
-/* Calls bufread with the specified arguments, and jumps to lossage if
-   the read fails. */
-#define assertive_bufread(a,b,c,d)             \
-       do                                      \
-         {                                     \
-           if (!bufread (a,b,c,d))             \
-             goto lossage;                     \
-         }                                     \
-       while (0)
-
-/* Reads the dictionary from file with handle H, and returns it in a
-   dictionary structure.  This dictionary may be modified in order to
-   rename, reorder, and delete variables, etc. */
-struct dictionary *
-sfm_read_dictionary (struct file_handle *h, struct sfm_read_info *inf)
+#define lose(X)                                 \
+       do {                                    \
+           corrupt_msg X;                      \
+           goto error;                         \
+       } while (0)
+
+/* Calls buf_read with the specified arguments, and jumps to
+   error if the read fails. */
+#define assertive_buf_read(a,b,c,d)             \
+       do {                                    \
+           if (!buf_read (a,b,c,d))            \
+             goto error;                       \
+       } while (0)
+
+/* Opens the system file designated by file handle FH for
+   reading.  Reads the system file's dictionary into *DICT.
+   If INFO is non-null, then it receives additional info about the
+   system file. */
+struct sfm_reader *
+sfm_open_reader (struct file_handle *fh, struct dictionary **dict,
+                 struct sfm_read_info *info)
 {
 {
-  /* The file handle extension record. */
-  struct sfm_fhuser_ext *ext;
-
-  /* Allows for quick reference to variables according to indexes
-     relative to position within a case. */
-  struct variable **var_by_index = NULL;
-
-  /* Check whether the file is already open. */
-  if (h->class == &sfm_r_class)
-    {
-      ext = h->ext;
-      ext->opened++;
-      return ext->dict;
-    }
-  else if (h->class != NULL)
-    {
-      msg (ME, _("Cannot read file %s as system file: already opened for %s."),
-          handle_get_name (h), h->class->name);
-      return NULL;
-    }
-
-  msg (VM (1), _("%s: Opening system-file handle %s for reading."),
-       handle_get_filename (h), handle_get_name (h));
-  
-  /* Open the physical disk file. */
-  ext = xmalloc (sizeof (struct sfm_fhuser_ext));
-  ext->file = fn_open (handle_get_filename (h), "rb");
-  if (ext->file == NULL)
+  struct sfm_reader *r = NULL;
+  struct variable **var_by_idx = NULL;
+
+  *dict = dict_create ();
+  if (!fh_open (fh, "system file", "rs"))
+    goto error;
+
+  /* Create and initialize reader. */
+  r = xmalloc (sizeof *r);
+  r->fh = fh;
+  r->file = fn_open (handle_get_filename (fh), "rb");
+
+  r->reverse_endian = 0;
+  r->fix_specials = 0;
+  r->value_cnt = 0;
+  r->case_cnt = 0;
+  r->compressed = 0;
+  r->bias = 100.0;
+  r->weight_idx = -1;
+
+  r->vars = NULL;
+  r->var_cnt = 0;
+
+  r->sysmis = -FLT64_MAX;
+  r->highest = FLT64_MAX;
+  r->lowest = second_lowest_flt64;
+
+  r->buf = r->ptr = r->end = NULL;
+  r->y = r->x + sizeof r->x;
+
+  /* Check that file open succeeded. */
+  if (r->file == NULL)
     {
       msg (ME, _("An error occurred while opening \"%s\" for reading "
                  "as a system file: %s."),
     {
       msg (ME, _("An error occurred while opening \"%s\" for reading "
                  "as a system file: %s."),
-           handle_get_filename (h), strerror (errno));
+           handle_get_filename (r->fh), strerror (errno));
       err_cond_fail ();
       err_cond_fail ();
-      free (ext);
-      return NULL;
+      goto error;
     }
 
     }
 
-  /* Initialize the sfm_fhuser_ext structure. */
-  h->class = &sfm_r_class;
-  h->ext = ext;
-  ext->dict = NULL;
-  ext->buf = ext->ptr = ext->end = NULL;
-  ext->y = ext->x + sizeof ext->x;
-  ext->opened = 1;
-
-  /* Default special constants. */
-  ext->sysmis = -FLT64_MAX;
-  ext->highest = FLT64_MAX;
-  ext->lowest = second_lowest_flt64;
-
-  /* Read the header. */
-  if (!read_header (h, inf))
-    goto lossage;
-
-  /* Read about the variables. */
-  if (!read_variables (h, &var_by_index))
-    goto lossage;
+  /* Read header and variables. */
+  if (!read_header (r, *dict, info) || !read_variables (r, *dict, &var_by_idx))
+    goto error;
 
   /* Handle weighting. */
 
   /* Handle weighting. */
-  if (ext->weight_index != -1)
+  if (r->weight_idx != -1)
     {
     {
-      struct variable *wv = var_by_index[ext->weight_index];
+      struct variable *weight_var = var_by_idx[r->weight_idx];
 
 
-      if (wv == NULL)
-       lose ((ME, _("%s: Weighting variable may not be a continuation of "
-              "a long string variable."), handle_get_filename (h)));
-      else if (wv->type == ALPHA)
+      if (weight_var == NULL)
+       lose ((ME,
+               _("%s: Weighting variable may not be a continuation of "
+              "a long string variable."), handle_get_filename (fh)));
+      else if (weight_var->type == ALPHA)
        lose ((ME, _("%s: Weighting variable may not be a string variable."),
        lose ((ME, _("%s: Weighting variable may not be a string variable."),
-              handle_get_filename (h)));
+              handle_get_filename (fh)));
 
 
-      dict_set_weight (ext->dict, wv);
+      dict_set_weight (*dict, weight_var);
     }
   else
     }
   else
-    dict_set_weight (ext->dict, NULL);
+    dict_set_weight (*dict, NULL);
 
   /* Read records of types 3, 4, 6, and 7. */
   for (;;)
     {
       int32 rec_type;
 
 
   /* Read records of types 3, 4, 6, and 7. */
   for (;;)
     {
       int32 rec_type;
 
-      assertive_bufread (h, &rec_type, sizeof rec_type, 0);
-      if (ext->reverse_endian)
+      assertive_buf_read (r, &rec_type, sizeof rec_type, 0);
+      if (r->reverse_endian)
        bswap_int32 (&rec_type);
 
       switch (rec_type)
        {
        case 3:
        bswap_int32 (&rec_type);
 
       switch (rec_type)
        {
        case 3:
-         if (!read_value_labels (h, var_by_index))
-           goto lossage;
+         if (!read_value_labels (r, *dict, var_by_idx))
+           goto error;
          break;
 
        case 4:
          lose ((ME, _("%s: Orphaned variable index record (type 4).  Type 4 "
                        "records must always immediately follow type 3 "
                        "records."),
          break;
 
        case 4:
          lose ((ME, _("%s: Orphaned variable index record (type 4).  Type 4 "
                        "records must always immediately follow type 3 "
                        "records."),
-                handle_get_filename (h)));
+                handle_get_filename (r->fh)));
 
        case 6:
 
        case 6:
-         if (!read_documents (h))
-           goto lossage;
+         if (!read_documents (r, *dict))
+           goto error;
          break;
 
        case 7:
          break;
 
        case 7:
@@ -332,8 +305,8 @@ sfm_read_dictionary (struct file_handle *h, struct sfm_read_info *inf)
 
            int skip = 0;
 
 
            int skip = 0;
 
-           assertive_bufread (h, &data, sizeof data, 0);
-           if (ext->reverse_endian)
+           assertive_buf_read (r, &data, sizeof data, 0);
+           if (r->reverse_endian)
              {
                bswap_int32 (&data.subtype);
                bswap_int32 (&data.size);
              {
                bswap_int32 (&data.subtype);
                bswap_int32 (&data.size);
@@ -343,13 +316,13 @@ sfm_read_dictionary (struct file_handle *h, struct sfm_read_info *inf)
            switch (data.subtype)
              {
              case 3:
            switch (data.subtype)
              {
              case 3:
-               if (!read_machine_int32_info (h, data.size, data.count))
-                 goto lossage;
+               if (!read_machine_int32_info (r, data.size, data.count))
+                 goto error;
                break;
 
              case 4:
                break;
 
              case 4:
-               if (!read_machine_flt64_info (h, data.size, data.count))
-                 goto lossage;
+               if (!read_machine_flt64_info (r, data.size, data.count))
+                 goto error;
                break;
 
              case 5:
                break;
 
              case 5:
@@ -361,15 +334,15 @@ sfm_read_dictionary (struct file_handle *h, struct sfm_read_info *inf)
              default:
                msg (MW, _("%s: Unrecognized record type 7, subtype %d "
                            "encountered in system file."),
              default:
                msg (MW, _("%s: Unrecognized record type 7, subtype %d "
                            "encountered in system file."),
-                     handle_get_filename (h), data.subtype);
+                     handle_get_filename (r->fh), data.subtype);
                skip = 1;
              }
 
            if (skip)
              {
                skip = 1;
              }
 
            if (skip)
              {
-               void *x = bufread (h, NULL, data.size * data.count, 0);
+               void *x = buf_read (r, NULL, data.size * data.count, 0);
                if (x == NULL)
                if (x == NULL)
-                 goto lossage;
+                 goto error;
                free (x);
              }
          }
                free (x);
              }
          }
@@ -379,43 +352,37 @@ sfm_read_dictionary (struct file_handle *h, struct sfm_read_info *inf)
          {
            int32 filler;
 
          {
            int32 filler;
 
-           assertive_bufread (h, &filler, sizeof filler, 0);
-           goto break_out_of_loop;
+           assertive_buf_read (r, &filler, sizeof filler, 0);
+           goto success;
          }
 
        default:
          lose ((ME, _("%s: Unrecognized record type %d."),
          }
 
        default:
          lose ((ME, _("%s: Unrecognized record type %d."),
-                 handle_get_filename (h), rec_type));
+                 handle_get_filename (r->fh), rec_type));
        }
     }
 
        }
     }
 
-break_out_of_loop:
+success:
   /* Come here on successful completion. */
   /* Come here on successful completion. */
-  msg (VM (2), _("Read system-file dictionary successfully."));
-    
-  free (var_by_index);
-  return ext->dict;
+  free (var_by_idx);
+  return r;
 
 
-lossage:
+error:
   /* Come here on unsuccessful completion. */
   /* Come here on unsuccessful completion. */
-  msg (VM (1), _("Error reading system-file header."));
-  
-  free (var_by_index);
-  fn_close (handle_get_filename (h), ext->file);
-  if (ext && ext->dict)
-    dict_destroy (ext->dict);
-  free (ext);
-  h->class = NULL;
-  h->ext = NULL;
+  sfm_close_reader (r);
+  free (var_by_idx);
+  if (*dict != NULL) 
+    {
+      dict_destroy (*dict);
+      *dict = NULL; 
+    }
   return NULL;
 }
 
 /* Read record type 7, subtype 3. */
 static int
   return NULL;
 }
 
 /* Read record type 7, subtype 3. */
 static int
-read_machine_int32_info (struct file_handle *h, int size, int count)
+read_machine_int32_info (struct sfm_reader *r, int size, int count)
 {
 {
-  struct sfm_fhuser_ext *ext = h->ext;
-
   int32 data[8];
   int file_bigendian;
 
   int32 data[8];
   int file_bigendian;
 
@@ -424,113 +391,105 @@ read_machine_int32_info (struct file_handle *h, int size, int count)
   if (size != sizeof (int32) || count != 8)
     lose ((ME, _("%s: Bad size (%d) or count (%d) field on record type 7, "
                  "subtype 3.   Expected size %d, count 8."),
   if (size != sizeof (int32) || count != 8)
     lose ((ME, _("%s: Bad size (%d) or count (%d) field on record type 7, "
                  "subtype 3.   Expected size %d, count 8."),
-          handle_get_filename (h), size, count, sizeof (int32)));
+          handle_get_filename (r->fh), size, count, sizeof (int32)));
 
 
-  assertive_bufread (h, data, sizeof data, 0);
-  if (ext->reverse_endian)
+  assertive_buf_read (r, data, sizeof data, 0);
+  if (r->reverse_endian)
     for (i = 0; i < 8; i++)
       bswap_int32 (&data[i]);
 
     for (i = 0; i < 8; i++)
       bswap_int32 (&data[i]);
 
-  /* PORTME: Check floating-point representation. */
 #ifdef FPREP_IEEE754
   if (data[4] != 1)
     lose ((ME, _("%s: Floating-point representation in system file is not "
                  "IEEE-754.  PSPP cannot convert between floating-point "
                  "formats."),
 #ifdef FPREP_IEEE754
   if (data[4] != 1)
     lose ((ME, _("%s: Floating-point representation in system file is not "
                  "IEEE-754.  PSPP cannot convert between floating-point "
                  "formats."),
-           handle_get_filename (h)));
+           handle_get_filename (r->fh)));
+#else
+#error Add support for your floating-point format.
 #endif
 
 #endif
 
-  /* PORTME: Check recorded file endianness against intuited file
-     endianness. */
 #ifdef WORDS_BIGENDIAN
   file_bigendian = 1;
 #else
   file_bigendian = 0;
 #endif
 #ifdef WORDS_BIGENDIAN
   file_bigendian = 1;
 #else
   file_bigendian = 0;
 #endif
-  if (ext->reverse_endian)
+  if (r->reverse_endian)
     file_bigendian ^= 1;
   if (file_bigendian ^ (data[6] == 1))
     file_bigendian ^= 1;
   if (file_bigendian ^ (data[6] == 1))
-    lose ((ME, _("%s: File-indicated endianness (%s) does not match endianness "
-                 "intuited from file header (%s)."),
-          handle_get_filename (h),
+    lose ((ME, _("%s: File-indicated endianness (%s) does not match "
+                 "endianness intuited from file header (%s)."),
+          handle_get_filename (r->fh),
            file_bigendian ? _("big-endian") : _("little-endian"),
           data[6] == 1 ? _("big-endian") : (data[6] == 2 ? _("little-endian")
                                          : _("unknown"))));
 
   /* PORTME: Character representation code. */
            file_bigendian ? _("big-endian") : _("little-endian"),
           data[6] == 1 ? _("big-endian") : (data[6] == 2 ? _("little-endian")
                                          : _("unknown"))));
 
   /* PORTME: Character representation code. */
-  if (data[7] != 2 && data[7] != 3)
-    lose ((ME, _("%s: File-indicated character representation code (%s) is not "
-                 "ASCII."),
-           handle_get_filename (h),
+  if (data[7] != 2 && data[7] != 3) 
+    lose ((ME, _("%s: File-indicated character representation code (%s) is "
+                 "not ASCII."),
+           handle_get_filename (r->fh),
            (data[7] == 1 ? "EBCDIC"
             : (data[7] == 4 ? _("DEC Kanji") : _("Unknown")))));
 
   return 1;
 
            (data[7] == 1 ? "EBCDIC"
             : (data[7] == 4 ? _("DEC Kanji") : _("Unknown")))));
 
   return 1;
 
-lossage:
+error:
   return 0;
 }
 
 /* Read record type 7, subtype 4. */
 static int
   return 0;
 }
 
 /* Read record type 7, subtype 4. */
 static int
-read_machine_flt64_info (struct file_handle *h, int size, int count)
+read_machine_flt64_info (struct sfm_reader *r, int size, int count)
 {
 {
-  struct sfm_fhuser_ext *ext = h->ext;
-
   flt64 data[3];
   flt64 data[3];
-
   int i;
 
   if (size != sizeof (flt64) || count != 3)
     lose ((ME, _("%s: Bad size (%d) or count (%d) field on record type 7, "
                  "subtype 4.   Expected size %d, count 8."),
   int i;
 
   if (size != sizeof (flt64) || count != 3)
     lose ((ME, _("%s: Bad size (%d) or count (%d) field on record type 7, "
                  "subtype 4.   Expected size %d, count 8."),
-          handle_get_filename (h), size, count, sizeof (flt64)));
+          handle_get_filename (r->fh), size, count, sizeof (flt64)));
 
 
-  assertive_bufread (h, data, sizeof data, 0);
-  if (ext->reverse_endian)
+  assertive_buf_read (r, data, sizeof data, 0);
+  if (r->reverse_endian)
     for (i = 0; i < 3; i++)
       bswap_flt64 (&data[i]);
 
   if (data[0] != SYSMIS || data[1] != FLT64_MAX
       || data[2] != second_lowest_flt64)
     {
     for (i = 0; i < 3; i++)
       bswap_flt64 (&data[i]);
 
   if (data[0] != SYSMIS || data[1] != FLT64_MAX
       || data[2] != second_lowest_flt64)
     {
-      ext->sysmis = data[0];
-      ext->highest = data[1];
-      ext->lowest = data[2];
+      r->sysmis = data[0];
+      r->highest = data[1];
+      r->lowest = data[2];
       msg (MW, _("%s: File-indicated value is different from internal value "
                 "for at least one of the three system values.  SYSMIS: "
                 "indicated %g, expected %g; HIGHEST: %g, %g; LOWEST: "
                 "%g, %g."),
       msg (MW, _("%s: File-indicated value is different from internal value "
                 "for at least one of the three system values.  SYSMIS: "
                 "indicated %g, expected %g; HIGHEST: %g, %g; LOWEST: "
                 "%g, %g."),
-          handle_get_filename (h), (double) data[0], (double) SYSMIS,
+          handle_get_filename (r->fh), (double) data[0], (double) SYSMIS,
           (double) data[1], (double) FLT64_MAX,
           (double) data[2], (double) second_lowest_flt64);
     }
   
   return 1;
 
           (double) data[1], (double) FLT64_MAX,
           (double) data[2], (double) second_lowest_flt64);
     }
   
   return 1;
 
-lossage:
+error:
   return 0;
 }
 
 static int
   return 0;
 }
 
 static int
-read_header (struct file_handle *h, struct sfm_read_info *inf)
+read_header (struct sfm_reader *r,
+             struct dictionary *dict, struct sfm_read_info *info)
 {
 {
-  struct sfm_fhuser_ext *ext = h->ext; /* File extension strcut. */
   struct sysfile_header hdr;           /* Disk buffer. */
   struct sysfile_header hdr;           /* Disk buffer. */
-  struct dictionary *dict;             /* File dictionary. */
   char prod_name[sizeof hdr.prod_name + 1];    /* Buffer for product name. */
   int skip_amt = 0;                    /* Amount of product name to omit. */
   int i;
 
   char prod_name[sizeof hdr.prod_name + 1];    /* Buffer for product name. */
   int skip_amt = 0;                    /* Amount of product name to omit. */
   int i;
 
-  /* Create the dictionary. */
-  dict = ext->dict = dict_create ();
-
   /* Read header, check magic. */
   /* Read header, check magic. */
-  assertive_bufread (h, &hdr, sizeof hdr, 0);
-  if (0 != strncmp ("$FL2", hdr.rec_type, 4))
+  assertive_buf_read (r, &hdr, sizeof hdr, 0);
+  if (strncmp ("$FL2", hdr.rec_type, 4) != 0)
     lose ((ME, _("%s: Bad magic.  Proper system files begin with "
                 "the four characters `$FL2'. This file will not be read."),
     lose ((ME, _("%s: Bad magic.  Proper system files begin with "
                 "the four characters `$FL2'. This file will not be read."),
-          handle_get_filename (h)));
+          handle_get_filename (r->fh)));
 
   /* Check eye-catcher string. */
   memcpy (prod_name, hdr.prod_name, sizeof hdr.prod_name);
 
   /* Check eye-catcher string. */
   memcpy (prod_name, hdr.prod_name, sizeof hdr.prod_name);
@@ -564,52 +523,52 @@ read_header (struct file_handle *h, struct sfm_read_info *inf)
   }
   
   /* Check endianness. */
   }
   
   /* Check endianness. */
-  /* PORTME: endianness. */
   if (hdr.layout_code == 2)
   if (hdr.layout_code == 2)
-    ext->reverse_endian = 0;
+    r->reverse_endian = 0;
   else
     {
       bswap_int32 (&hdr.layout_code);
       if (hdr.layout_code != 2)
        lose ((ME, _("%s: File layout code has unexpected value %d.  Value "
                      "should be 2, in big-endian or little-endian format."),
   else
     {
       bswap_int32 (&hdr.layout_code);
       if (hdr.layout_code != 2)
        lose ((ME, _("%s: File layout code has unexpected value %d.  Value "
                      "should be 2, in big-endian or little-endian format."),
-              handle_get_filename (h), hdr.layout_code));
+              handle_get_filename (r->fh), hdr.layout_code));
 
 
-      ext->reverse_endian = 1;
+      r->reverse_endian = 1;
       bswap_int32 (&hdr.case_size);
       bswap_int32 (&hdr.case_size);
-      bswap_int32 (&hdr.compressed);
-      bswap_int32 (&hdr.weight_index);
-      bswap_int32 (&hdr.ncases);
+      bswap_int32 (&hdr.compress);
+      bswap_int32 (&hdr.weight_idx);
+      bswap_int32 (&hdr.case_cnt);
       bswap_flt64 (&hdr.bias);
     }
 
   /* Copy basic info and verify correctness. */
       bswap_flt64 (&hdr.bias);
     }
 
   /* Copy basic info and verify correctness. */
-  ext->case_size = hdr.case_size;
-  if (hdr.case_size <= 0 || ext->case_size > (INT_MAX
-                                             / (int) sizeof (union value) / 2))
+  r->value_cnt = hdr.case_size;
+  if (r->value_cnt <= 0
+      || r->value_cnt > (INT_MAX / (int) sizeof (union value) / 2))
     lose ((ME, _("%s: Number of elements per case (%d) is not between 1 "
                  "and %d."),
     lose ((ME, _("%s: Number of elements per case (%d) is not between 1 "
                  "and %d."),
-           handle_get_filename (h), hdr.case_size,
+           handle_get_filename (r->fh), r->value_cnt,
            INT_MAX / sizeof (union value) / 2));
 
            INT_MAX / sizeof (union value) / 2));
 
-  ext->compressed = hdr.compressed;
+  r->compressed = hdr.compress;
 
 
-  ext->weight_index = hdr.weight_index - 1;
-  if (hdr.weight_index < 0 || hdr.weight_index > hdr.case_size)
+  r->weight_idx = hdr.weight_idx - 1;
+  if (hdr.weight_idx < 0 || hdr.weight_idx > r->value_cnt)
     lose ((ME, _("%s: Index of weighting variable (%d) is not between 0 "
                  "and number of elements per case (%d)."),
     lose ((ME, _("%s: Index of weighting variable (%d) is not between 0 "
                  "and number of elements per case (%d)."),
-          handle_get_filename (h), hdr.weight_index, ext->case_size));
+          handle_get_filename (r->fh), hdr.weight_idx, r->value_cnt));
 
 
-  ext->ncases = hdr.ncases;
-  if (ext->ncases < -1 || ext->ncases > INT_MAX / 2)
-    lose ((ME, _("%s: Number of cases in file (%ld) is not between -1 and "
-          "%d."), handle_get_filename (h), (long) ext->ncases, INT_MAX / 2));
+  r->case_cnt = hdr.case_cnt;
+  if (r->case_cnt < -1 || r->case_cnt > INT_MAX / 2)
+    lose ((ME,
+           _("%s: Number of cases in file (%ld) is not between -1 and %d."),
+           handle_get_filename (r->fh), (long) r->case_cnt, INT_MAX / 2));
 
 
-  ext->bias = hdr.bias;
-  if (ext->bias != 100.0)
+  r->bias = hdr.bias;
+  if (r->bias != 100.0)
     corrupt_msg (MW, _("%s: Compression bias (%g) is not the usual "
                        "value of 100."),
     corrupt_msg (MW, _("%s: Compression bias (%g) is not the usual "
                        "value of 100."),
-                 handle_get_filename (h), ext->bias);
+                 handle_get_filename (r->fh), r->bias);
 
   /* Make a file label only on the condition that the given label is
      not all spaces or nulls. */
 
   /* Make a file label only on the condition that the given label is
      not all spaces or nulls. */
@@ -629,68 +588,67 @@ read_header (struct file_handle *h, struct sfm_read_info *inf)
        }
   }
 
        }
   }
 
-  if (inf)
+  if (info)
     {
       char *cp;
 
     {
       char *cp;
 
-      memcpy (inf->creation_date, hdr.creation_date, 9);
-      inf->creation_date[9] = 0;
+      memcpy (info->creation_date, hdr.creation_date, 9);
+      info->creation_date[9] = 0;
 
 
-      memcpy (inf->creation_time, hdr.creation_time, 8);
-      inf->creation_time[8] = 0;
+      memcpy (info->creation_time, hdr.creation_time, 8);
+      info->creation_time[8] = 0;
 
 #ifdef WORDS_BIGENDIAN
 
 #ifdef WORDS_BIGENDIAN
-      inf->bigendian = !ext->reverse_endian;
+      info->big_endian = !r->reverse_endian;
 #else
 #else
-      inf->bigendian = ext->reverse_endian;
+      info->big_endian = r->reverse_endian;
 #endif
 
 #endif
 
-      inf->compressed = hdr.compressed;
+      info->compressed = hdr.compress;
 
 
-      inf->ncases = hdr.ncases;
+      info->case_cnt = hdr.case_cnt;
 
       for (cp = &prod_name[skip_amt]; cp < &prod_name[60]; cp++)
        if (isgraph ((unsigned char) *cp))
          break;
 
       for (cp = &prod_name[skip_amt]; cp < &prod_name[60]; cp++)
        if (isgraph ((unsigned char) *cp))
          break;
-      strcpy (inf->product, cp);
+      strcpy (info->product, cp);
     }
 
   return 1;
 
     }
 
   return 1;
 
-lossage:
+error:
   return 0;
 }
 
 /* Reads most of the dictionary from file H; also fills in the
   return 0;
 }
 
 /* Reads most of the dictionary from file H; also fills in the
-   associated VAR_BY_INDEX array.  The get.* elements in the
-   created dictionary are set to appropriate values to allow the
-   file to be read.  */
+   associated VAR_BY_IDX array. */
 static int
 static int
-read_variables (struct file_handle *h, struct variable ***var_by_index)
+read_variables (struct sfm_reader *r,
+                struct dictionary *dict, struct variable ***var_by_idx)
 {
   int i;
 
 {
   int i;
 
-  struct sfm_fhuser_ext *ext = h->ext; /* File extension record. */
-  struct dictionary *dict = ext->dict; /* Dictionary being constructed. */
   struct sysfile_variable sv;          /* Disk buffer. */
   int long_string_count = 0;   /* # of long string continuation
                                   records still expected. */
   int next_value = 0;          /* Index to next `value' structure. */
   struct sysfile_variable sv;          /* Disk buffer. */
   int long_string_count = 0;   /* # of long string continuation
                                   records still expected. */
   int next_value = 0;          /* Index to next `value' structure. */
+  size_t var_cap = 0;
 
   /* Allocate variables. */
 
   /* Allocate variables. */
-  *var_by_index = xmalloc (sizeof **var_by_index * ext->case_size);
+  *var_by_idx = xmalloc (sizeof **var_by_idx * r->value_cnt);
 
   /* Read in the entry for each variable and use the info to
      initialize the dictionary. */
 
   /* Read in the entry for each variable and use the info to
      initialize the dictionary. */
-  for (i = 0; i < ext->case_size; i++)
+  for (i = 0; i < r->value_cnt; i++)
     {
       struct variable *vv;
       char name[9];
     {
       struct variable *vv;
       char name[9];
+      int nv;
       int j;
 
       int j;
 
-      assertive_bufread (h, &sv, sizeof sv, 0);
+      assertive_buf_read (r, &sv, sizeof sv, 0);
 
 
-      if (ext->reverse_endian)
+      if (r->reverse_endian)
        {
          bswap_int32 (&sv.rec_type);
          bswap_int32 (&sv.type);
        {
          bswap_int32 (&sv.rec_type);
          bswap_int32 (&sv.type);
@@ -703,7 +661,7 @@ read_variables (struct file_handle *h, struct variable ***var_by_index)
       if (sv.rec_type != 2)
        lose ((ME, _("%s: position %d: Bad record type (%d); "
                      "the expected value was 2."),
       if (sv.rec_type != 2)
        lose ((ME, _("%s: position %d: Bad record type (%d); "
                      "the expected value was 2."),
-               handle_get_filename (h), i, sv.rec_type));
+               handle_get_filename (r->fh), i, sv.rec_type));
 
       /* If there was a long string previously, make sure that the
         continuations are present; otherwise make sure there aren't
 
       /* If there was a long string previously, make sure that the
         continuations are present; otherwise make sure there aren't
@@ -713,44 +671,44 @@ read_variables (struct file_handle *h, struct variable ***var_by_index)
          if (sv.type != -1)
            lose ((ME, _("%s: position %d: String variable does not have "
                         "proper number of continuation records."),
          if (sv.type != -1)
            lose ((ME, _("%s: position %d: String variable does not have "
                         "proper number of continuation records."),
-                   handle_get_filename (h), i));
+                   handle_get_filename (r->fh), i));
 
 
-         (*var_by_index)[i] = NULL;
+         (*var_by_idx)[i] = NULL;
          long_string_count--;
          continue;
        }
       else if (sv.type == -1)
        lose ((ME, _("%s: position %d: Superfluous long string continuation "
                      "record."),
          long_string_count--;
          continue;
        }
       else if (sv.type == -1)
        lose ((ME, _("%s: position %d: Superfluous long string continuation "
                      "record."),
-               handle_get_filename (h), i));
+               handle_get_filename (r->fh), i));
 
       /* Check fields for validity. */
       if (sv.type < 0 || sv.type > 255)
        lose ((ME, _("%s: position %d: Bad variable type code %d."),
 
       /* Check fields for validity. */
       if (sv.type < 0 || sv.type > 255)
        lose ((ME, _("%s: position %d: Bad variable type code %d."),
-              handle_get_filename (h), i, sv.type));
+              handle_get_filename (r->fh), i, sv.type));
       if (sv.has_var_label != 0 && sv.has_var_label != 1)
        lose ((ME, _("%s: position %d: Variable label indicator field is not "
       if (sv.has_var_label != 0 && sv.has_var_label != 1)
        lose ((ME, _("%s: position %d: Variable label indicator field is not "
-              "0 or 1."), handle_get_filename (h), i));
+              "0 or 1."), handle_get_filename (r->fh), i));
       if (sv.n_missing_values < -3 || sv.n_missing_values > 3
          || sv.n_missing_values == -1)
        lose ((ME, _("%s: position %d: Missing value indicator field is not "
       if (sv.n_missing_values < -3 || sv.n_missing_values > 3
          || sv.n_missing_values == -1)
        lose ((ME, _("%s: position %d: Missing value indicator field is not "
-                    "-3, -2, 0, 1, 2, or 3."), handle_get_filename (h), i));
+                    "-3, -2, 0, 1, 2, or 3."), handle_get_filename (r->fh), i));
 
       /* Copy first character of variable name. */
       if (!isalpha ((unsigned char) sv.name[0])
          && sv.name[0] != '@' && sv.name[0] != '#')
        lose ((ME, _("%s: position %d: Variable name begins with invalid "
                      "character."),
 
       /* Copy first character of variable name. */
       if (!isalpha ((unsigned char) sv.name[0])
          && sv.name[0] != '@' && sv.name[0] != '#')
        lose ((ME, _("%s: position %d: Variable name begins with invalid "
                      "character."),
-               handle_get_filename (h), i));
+               handle_get_filename (r->fh), i));
       if (islower ((unsigned char) sv.name[0]))
        msg (MW, _("%s: position %d: Variable name begins with lowercase letter "
                    "%c."),
       if (islower ((unsigned char) sv.name[0]))
        msg (MW, _("%s: position %d: Variable name begins with lowercase letter "
                    "%c."),
-             handle_get_filename (h), i, sv.name[0]);
+             handle_get_filename (r->fh), i, sv.name[0]);
       if (sv.name[0] == '#')
        msg (MW, _("%s: position %d: Variable name begins with octothorpe "
                   "(`#').  Scratch variables should not appear in system "
                   "files."),
       if (sv.name[0] == '#')
        msg (MW, _("%s: position %d: Variable name begins with octothorpe "
                   "(`#').  Scratch variables should not appear in system "
                   "files."),
-             handle_get_filename (h), i);
+             handle_get_filename (r->fh), i);
       name[0] = toupper ((unsigned char) (sv.name[0]));
 
       /* Copy remaining characters of variable name. */
       name[0] = toupper ((unsigned char) (sv.name[0]));
 
       /* Copy remaining characters of variable name. */
@@ -764,7 +722,7 @@ read_variables (struct file_handle *h, struct variable ***var_by_index)
            {
              msg (MW, _("%s: position %d: Variable name character %d is "
                          "lowercase letter %c."),
            {
              msg (MW, _("%s: position %d: Variable name character %d is "
                          "lowercase letter %c."),
-                   handle_get_filename (h), i, j + 1, sv.name[j]);
+                   handle_get_filename (r->fh), i, j + 1, sv.name[j]);
              name[j] = toupper ((unsigned char) (c));
            }
          else if (isalnum (c) || c == '.' || c == '@'
              name[j] = toupper ((unsigned char) (c));
            }
          else if (isalnum (c) || c == '.' || c == '@'
@@ -773,24 +731,20 @@ read_variables (struct file_handle *h, struct variable ***var_by_index)
          else
            lose ((ME, _("%s: position %d: character `\\%03o' (%c) is not valid in a "
                          "variable name."),
          else
            lose ((ME, _("%s: position %d: character `\\%03o' (%c) is not valid in a "
                          "variable name."),
-                   handle_get_filename (h), i, c, c));
+                   handle_get_filename (r->fh), i, c, c));
        }
       name[j] = 0;
 
       /* Create variable. */
        }
       name[j] = 0;
 
       /* Create variable. */
-      vv = (*var_by_index)[i] = dict_create_var (dict, name, sv.type);
+      vv = (*var_by_idx)[i] = dict_create_var (dict, name, sv.type);
       if (vv == NULL) 
         lose ((ME, _("%s: Duplicate variable name `%s' within system file."),
       if (vv == NULL) 
         lose ((ME, _("%s: Duplicate variable name `%s' within system file."),
-               handle_get_filename (h), name));
+               handle_get_filename (r->fh), name));
 
       /* Case reading data. */
 
       /* Case reading data. */
-      vv->get.fv = next_value;
-      if (sv.type == 0) 
-        vv->get.nv = 1;
-      else
-        vv->get.nv = DIV_RND_UP (sv.type, sizeof (flt64));
-      long_string_count = vv->get.nv - 1;
-      next_value += vv->get.nv;
+      nv = sv.type == 0 ? 1 : DIV_RND_UP (sv.type, sizeof (flt64));
+      long_string_count = nv - 1;
+      next_value += nv;
 
       /* Get variable label, if any. */
       if (sv.has_var_label == 1)
 
       /* Get variable label, if any. */
       if (sv.has_var_label == 1)
@@ -799,20 +753,20 @@ read_variables (struct file_handle *h, struct variable ***var_by_index)
          int32 len;
 
          /* Read length of label. */
          int32 len;
 
          /* Read length of label. */
-         assertive_bufread (h, &len, sizeof len, 0);
-         if (ext->reverse_endian)
+         assertive_buf_read (r, &len, sizeof len, 0);
+         if (r->reverse_endian)
            bswap_int32 (&len);
 
          /* Check len. */
          if (len < 0 || len > 255)
            lose ((ME, _("%s: Variable %s indicates variable label of invalid "
                          "length %d."),
            bswap_int32 (&len);
 
          /* Check len. */
          if (len < 0 || len > 255)
            lose ((ME, _("%s: Variable %s indicates variable label of invalid "
                          "length %d."),
-                   handle_get_filename (h), vv->name, len));
+                   handle_get_filename (r->fh), vv->name, len));
 
          /* Read label into variable structure. */
 
          /* Read label into variable structure. */
-         vv->label = bufread (h, NULL, ROUND_UP (len, sizeof (int32)), len + 1);
+         vv->label = buf_read (r, NULL, ROUND_UP (len, sizeof (int32)), len + 1);
          if (vv->label == NULL)
          if (vv->label == NULL)
-           goto lossage;
+           goto error;
          vv->label[len] = '\0';
        }
 
          vv->label[len] = '\0';
        }
 
@@ -824,11 +778,11 @@ read_variables (struct file_handle *h, struct variable ***var_by_index)
          if (vv->width > MAX_SHORT_STRING)
            lose ((ME, _("%s: Long string variable %s may not have missing "
                          "values."),
          if (vv->width > MAX_SHORT_STRING)
            lose ((ME, _("%s: Long string variable %s may not have missing "
                          "values."),
-                   handle_get_filename (h), vv->name));
+                   handle_get_filename (r->fh), vv->name));
 
 
-         assertive_bufread (h, mv, sizeof *mv * abs (sv.n_missing_values), 0);
+         assertive_buf_read (r, mv, sizeof *mv * abs (sv.n_missing_values), 0);
 
 
-         if (ext->reverse_endian && vv->type == NUMERIC)
+         if (r->reverse_endian && vv->type == NUMERIC)
            for (j = 0; j < abs (sv.n_missing_values); j++)
              bswap_flt64 (&mv[j]);
 
            for (j = 0; j < abs (sv.n_missing_values); j++)
              bswap_flt64 (&mv[j]);
 
@@ -849,14 +803,14 @@ read_variables (struct file_handle *h, struct variable ***var_by_index)
              if (vv->type == ALPHA)
                lose ((ME, _("%s: String variable %s may not have missing "
                              "values specified as a range."),
              if (vv->type == ALPHA)
                lose ((ME, _("%s: String variable %s may not have missing "
                              "values specified as a range."),
-                       handle_get_filename (h), vv->name));
+                       handle_get_filename (r->fh), vv->name));
 
 
-             if (mv[0] == ext->lowest)
+             if (mv[0] == r->lowest)
                {
                  vv->miss_type = MISSING_LOW;
                  vv->missing[x++].f = mv[1];
                }
                {
                  vv->miss_type = MISSING_LOW;
                  vv->missing[x++].f = mv[1];
                }
-             else if (mv[1] == ext->highest)
+             else if (mv[1] == r->highest)
                {
                  vv->miss_type = MISSING_HIGH;
                  vv->missing[x++].f = mv[0];
                {
                  vv->miss_type = MISSING_HIGH;
                  vv->missing[x++].f = mv[0];
@@ -878,39 +832,46 @@ read_variables (struct file_handle *h, struct variable ***var_by_index)
       else
        vv->miss_type = MISSING_NONE;
 
       else
        vv->miss_type = MISSING_NONE;
 
-      if (!parse_format_spec (h, sv.print, &vv->print, vv)
-         || !parse_format_spec (h, sv.write, &vv->write, vv))
-       goto lossage;
+      if (!parse_format_spec (r, sv.print, &vv->print, vv)
+         || !parse_format_spec (r, sv.write, &vv->write, vv))
+       goto error;
+
+      /* Add variable to list. */
+      if (var_cap >= r->var_cnt) 
+        {
+          var_cap = 2 + r->var_cnt * 2;
+          r->vars = xrealloc (r->vars, var_cap * sizeof *r->vars);
+        }
+      r->vars[r->var_cnt].width = vv->width;
+      r->vars[r->var_cnt].fv = vv->fv;
+      r->var_cnt++;
     }
 
   /* Some consistency checks. */
   if (long_string_count != 0)
     lose ((ME, _("%s: Long string continuation records omitted at end of "
                  "dictionary."),
     }
 
   /* Some consistency checks. */
   if (long_string_count != 0)
     lose ((ME, _("%s: Long string continuation records omitted at end of "
                  "dictionary."),
-           handle_get_filename (h)));
-  if (next_value != ext->case_size)
+           handle_get_filename (r->fh)));
+  if (next_value != r->value_cnt)
     lose ((ME, _("%s: System file header indicates %d variable positions but "
                  "%d were read from file."),
     lose ((ME, _("%s: System file header indicates %d variable positions but "
                  "%d were read from file."),
-           handle_get_filename (h), ext->case_size, next_value));
+           handle_get_filename (r->fh), r->value_cnt, next_value));
 
   return 1;
 
 
   return 1;
 
-lossage:
-  dict_destroy (dict);
-  ext->dict = NULL;
-
+error:
   return 0;
 }
 
 /* Translates the format spec from sysfile format to internal
    format. */
 static int
   return 0;
 }
 
 /* Translates the format spec from sysfile format to internal
    format. */
 static int
-parse_format_spec (struct file_handle *h, int32 s, struct fmt_spec *v, struct variable *vv)
+parse_format_spec (struct sfm_reader *r, int32 s, struct fmt_spec *v, struct variable *vv)
 {
   v->type = translate_fmt ((s >> 16) & 0xff);
   if (v->type == -1)
     lose ((ME, _("%s: Bad format specifier byte (%d)."),
 {
   v->type = translate_fmt ((s >> 16) & 0xff);
   if (v->type == -1)
     lose ((ME, _("%s: Bad format specifier byte (%d)."),
-          handle_get_filename (h), (s >> 16) & 0xff));
+          handle_get_filename (r->fh), (s >> 16) & 0xff));
   v->w = (s >> 8) & 0xff;
   v->d = s & 0xff;
 
   v->w = (s >> 8) & 0xff;
   v->d = s & 0xff;
 
@@ -918,27 +879,26 @@ parse_format_spec (struct file_handle *h, int32 s, struct fmt_spec *v, struct va
 
   if (v->type == -1)
     lose ((ME, _("%s: Bad format specifier byte (%d)."),
 
   if (v->type == -1)
     lose ((ME, _("%s: Bad format specifier byte (%d)."),
-          handle_get_filename (h), (s >> 16) & 0xff));
+          handle_get_filename (r->fh), (s >> 16) & 0xff));
   if ((vv->type == ALPHA) ^ ((formats[v->type].cat & FCAT_STRING) != 0))
     lose ((ME, _("%s: %s variable %s has %s format specifier %s."),
   if ((vv->type == ALPHA) ^ ((formats[v->type].cat & FCAT_STRING) != 0))
     lose ((ME, _("%s: %s variable %s has %s format specifier %s."),
-          handle_get_filename (h),
+          handle_get_filename (r->fh),
            vv->type == ALPHA ? _("String") : _("Numeric"),
           vv->name,
           formats[v->type].cat & FCAT_STRING ? _("string") : _("numeric"),
           formats[v->type].name));
   return 1;
 
            vv->type == ALPHA ? _("String") : _("Numeric"),
           vv->name,
           formats[v->type].cat & FCAT_STRING ? _("string") : _("numeric"),
           formats[v->type].name));
   return 1;
 
-lossage:
+error:
   return 0;
 }
 
 /* Reads value labels from sysfile H and inserts them into the
    associated dictionary. */
 int
   return 0;
 }
 
 /* Reads value labels from sysfile H and inserts them into the
    associated dictionary. */
 int
-read_value_labels (struct file_handle *h, struct variable **var_by_index)
+read_value_labels (struct sfm_reader *r,
+                   struct dictionary *dict, struct variable **var_by_idx)
 {
 {
-  struct sfm_fhuser_ext *ext = h->ext; /* File extension record. */
-
   struct label 
     {
       unsigned char raw_value[8]; /* Value as uninterpreted bytes. */
   struct label 
     {
       unsigned char raw_value[8]; /* Value as uninterpreted bytes. */
@@ -959,8 +919,8 @@ read_value_labels (struct file_handle *h, struct variable **var_by_index)
      don't know yet whether it is of numeric or string type. */
 
   /* Read number of labels. */
      don't know yet whether it is of numeric or string type. */
 
   /* Read number of labels. */
-  assertive_bufread (h, &n_labels, sizeof n_labels, 0);
-  if (ext->reverse_endian)
+  assertive_buf_read (r, &n_labels, sizeof n_labels, 0);
+  if (r->reverse_endian)
     bswap_int32 (&n_labels);
 
   /* Allocate memory. */
     bswap_int32 (&n_labels);
 
   /* Allocate memory. */
@@ -976,15 +936,15 @@ read_value_labels (struct file_handle *h, struct variable **var_by_index)
       size_t padded_len;
 
       /* Read value. */
       size_t padded_len;
 
       /* Read value. */
-      assertive_bufread (h, label->raw_value, sizeof label->raw_value, 0);
+      assertive_buf_read (r, label->raw_value, sizeof label->raw_value, 0);
 
       /* Read label length. */
 
       /* Read label length. */
-      assertive_bufread (h, &label_len, sizeof label_len, 0);
+      assertive_buf_read (r, &label_len, sizeof label_len, 0);
       padded_len = ROUND_UP (label_len + 1, sizeof (flt64));
 
       /* Read label, padding. */
       label->label = xmalloc (padded_len + 1);
       padded_len = ROUND_UP (label_len + 1, sizeof (flt64));
 
       /* Read label, padding. */
       label->label = xmalloc (padded_len + 1);
-      assertive_bufread (h, label->label, padded_len - 1, 0);
+      assertive_buf_read (r, label->label, padded_len - 1, 0);
       label->label[label_len] = 0;
     }
 
       label->label[label_len] = 0;
     }
 
@@ -995,53 +955,53 @@ read_value_labels (struct file_handle *h, struct variable **var_by_index)
   {
     int32 rec_type;
     
   {
     int32 rec_type;
     
-    assertive_bufread (h, &rec_type, sizeof rec_type, 0);
-    if (ext->reverse_endian)
+    assertive_buf_read (r, &rec_type, sizeof rec_type, 0);
+    if (r->reverse_endian)
       bswap_int32 (&rec_type);
     
     if (rec_type != 4)
       lose ((ME, _("%s: Variable index record (type 4) does not immediately "
                    "follow value label record (type 3) as it should."),
       bswap_int32 (&rec_type);
     
     if (rec_type != 4)
       lose ((ME, _("%s: Variable index record (type 4) does not immediately "
                    "follow value label record (type 3) as it should."),
-             handle_get_filename (h)));
+             handle_get_filename (r->fh)));
   }
 
   /* Read number of variables associated with value label from type 4
      record. */
   }
 
   /* Read number of variables associated with value label from type 4
      record. */
-  assertive_bufread (h, &n_vars, sizeof n_vars, 0);
-  if (ext->reverse_endian)
+  assertive_buf_read (r, &n_vars, sizeof n_vars, 0);
+  if (r->reverse_endian)
     bswap_int32 (&n_vars);
     bswap_int32 (&n_vars);
-  if (n_vars < 1 || n_vars > dict_get_var_cnt (ext->dict))
+  if (n_vars < 1 || n_vars > dict_get_var_cnt (dict))
     lose ((ME, _("%s: Number of variables associated with a value label (%d) "
                  "is not between 1 and the number of variables (%d)."),
     lose ((ME, _("%s: Number of variables associated with a value label (%d) "
                  "is not between 1 and the number of variables (%d)."),
-          handle_get_filename (h), n_vars, dict_get_var_cnt (ext->dict)));
+          handle_get_filename (r->fh), n_vars, dict_get_var_cnt (dict)));
 
   /* Read the list of variables. */
   var = xmalloc (n_vars * sizeof *var);
   for (i = 0; i < n_vars; i++)
     {
 
   /* Read the list of variables. */
   var = xmalloc (n_vars * sizeof *var);
   for (i = 0; i < n_vars; i++)
     {
-      int32 var_index;
+      int32 var_idx;
       struct variable *v;
 
       /* Read variable index, check range. */
       struct variable *v;
 
       /* Read variable index, check range. */
-      assertive_bufread (h, &var_index, sizeof var_index, 0);
-      if (ext->reverse_endian)
-       bswap_int32 (&var_index);
-      if (var_index < 1 || var_index > ext->case_size)
+      assertive_buf_read (r, &var_idx, sizeof var_idx, 0);
+      if (r->reverse_endian)
+       bswap_int32 (&var_idx);
+      if (var_idx < 1 || var_idx > r->value_cnt)
        lose ((ME, _("%s: Variable index associated with value label (%d) is "
                      "not between 1 and the number of values (%d)."),
        lose ((ME, _("%s: Variable index associated with value label (%d) is "
                      "not between 1 and the number of values (%d)."),
-              handle_get_filename (h), var_index, ext->case_size));
+              handle_get_filename (r->fh), var_idx, r->value_cnt));
 
       /* Make sure it's a real variable. */
 
       /* Make sure it's a real variable. */
-      v = var_by_index[var_index - 1];
+      v = var_by_idx[var_idx - 1];
       if (v == NULL)
        lose ((ME, _("%s: Variable index associated with value label (%d) "
                      "refers to a continuation of a string variable, not to "
                      "an actual variable."),
       if (v == NULL)
        lose ((ME, _("%s: Variable index associated with value label (%d) "
                      "refers to a continuation of a string variable, not to "
                      "an actual variable."),
-               handle_get_filename (h), var_index));
+               handle_get_filename (r->fh), var_idx));
       if (v->type == ALPHA && v->width > MAX_SHORT_STRING)
        lose ((ME, _("%s: Value labels are not allowed on long string "
                      "variables (%s)."),
       if (v->type == ALPHA && v->width > MAX_SHORT_STRING)
        lose ((ME, _("%s: Value labels are not allowed on long string "
                      "variables (%s)."),
-               handle_get_filename (h), v->name));
+               handle_get_filename (r->fh), v->name));
 
       /* Add it to the list of variables. */
       var[i] = v;
 
       /* Add it to the list of variables. */
       var[i] = v;
@@ -1053,7 +1013,7 @@ read_value_labels (struct file_handle *h, struct variable **var_by_index)
       lose ((ME, _("%s: Variables associated with value label are not all of "
                    "identical type.  Variable %s has %s type, but variable "
                    "%s has %s type."),
       lose ((ME, _("%s: Variables associated with value label are not all of "
                    "identical type.  Variable %s has %s type, but variable "
                    "%s has %s type."),
-             handle_get_filename (h),
+             handle_get_filename (r->fh),
             var[0]->name, var[0]->type == ALPHA ? _("string") : _("numeric"),
             var[i]->name, var[i]->type == ALPHA ? _("string") : _("numeric")));
 
             var[0]->name, var[0]->type == ALPHA ? _("string") : _("numeric"),
             var[i]->name, var[i]->type == ALPHA ? _("string") : _("numeric")));
 
@@ -1071,7 +1031,7 @@ read_value_labels (struct file_handle *h, struct variable **var_by_index)
           flt64 f;
           assert (sizeof f == sizeof label->raw_value);
           memcpy (&f, label->raw_value, sizeof f);
           flt64 f;
           assert (sizeof f == sizeof label->raw_value);
           memcpy (&f, label->raw_value, sizeof f);
-          if (ext->reverse_endian)
+          if (r->reverse_endian)
             bswap_flt64 (&f);
           label->value.f = f;
         }
             bswap_flt64 (&f);
           label->value.f = f;
         }
@@ -1093,11 +1053,11 @@ read_value_labels (struct file_handle *h, struct variable **var_by_index)
          if (var[0]->type == NUMERIC)
            msg (MW, _("%s: File contains duplicate label for value %g for "
                        "variable %s."),
          if (var[0]->type == NUMERIC)
            msg (MW, _("%s: File contains duplicate label for value %g for "
                        "variable %s."),
-                 handle_get_filename (h), label->value.f, v->name);
+                 handle_get_filename (r->fh), label->value.f, v->name);
          else
            msg (MW, _("%s: File contains duplicate label for value `%.*s' "
                        "for variable %s."),
          else
            msg (MW, _("%s: File contains duplicate label for value `%.*s' "
                        "for variable %s."),
-                 handle_get_filename (h), v->width, label->value.s, v->name);
+                 handle_get_filename (r->fh), v->width, label->value.s, v->name);
        }
     }
 
        }
     }
 
@@ -1107,7 +1067,7 @@ read_value_labels (struct file_handle *h, struct variable **var_by_index)
   free (var);
   return 1;
 
   free (var);
   return 1;
 
-lossage:
+error:
   if (labels) 
     {
       for (i = 0; i < n_labels; i++)
   if (labels) 
     {
       for (i = 0; i < n_labels; i++)
@@ -1118,62 +1078,58 @@ lossage:
   return 0;
 }
 
   return 0;
 }
 
-/* Reads NBYTES bytes from the file represented by H.  If BUF is
+/* Reads BYTE_CNT bytes from the file represented by H.  If BUF is
    non-NULL, uses that as the buffer; otherwise allocates at least
    non-NULL, uses that as the buffer; otherwise allocates at least
-   MINALLOC bytes.  Returns a pointer to the buffer on success, NULL
+   MIN_ALLOC bytes.  Returns a pointer to the buffer on success, NULL
    on failure. */
 static void *
    on failure. */
 static void *
-bufread (struct file_handle *h, void *buf, size_t nbytes, size_t minalloc)
+buf_read (struct sfm_reader *r, void *buf, size_t byte_cnt, size_t min_alloc)
 {
 {
-  struct sfm_fhuser_ext *ext = h->ext;
-
   if (buf == NULL)
   if (buf == NULL)
-    buf = xmalloc (max (nbytes, minalloc));
-  if (1 != fread (buf, nbytes, 1, ext->file))
+    buf = xmalloc (max (byte_cnt, min_alloc));
+  if (1 != fread (buf, byte_cnt, 1, r->file))
     {
     {
-      if (ferror (ext->file))
+      if (ferror (r->file))
        msg (ME, _("%s: Reading system file: %s."),
        msg (ME, _("%s: Reading system file: %s."),
-             handle_get_filename (h), strerror (errno));
+             handle_get_filename (r->fh), strerror (errno));
       else
        corrupt_msg (ME, _("%s: Unexpected end of file."),
       else
        corrupt_msg (ME, _("%s: Unexpected end of file."),
-                     handle_get_filename (h));
+                     handle_get_filename (r->fh));
       return NULL;
     }
   return buf;
 }
 
       return NULL;
     }
   return buf;
 }
 
-/* Reads a document record, type 6, from system file H, and sets up
+/* Reads a document record, type 6, from system file R, and sets up
    the documents and n_documents fields in the associated
    dictionary. */
 static int
    the documents and n_documents fields in the associated
    dictionary. */
 static int
-read_documents (struct file_handle *h)
+read_documents (struct sfm_reader *r, struct dictionary *dict)
 {
 {
-  struct sfm_fhuser_ext *ext = h->ext;
-  struct dictionary *dict = ext->dict;
-  int32 n_lines;
+  int32 line_cnt;
   char *documents;
 
   if (dict_get_documents (dict) != NULL)
     lose ((ME, _("%s: System file contains multiple "
                  "type 6 (document) records."),
   char *documents;
 
   if (dict_get_documents (dict) != NULL)
     lose ((ME, _("%s: System file contains multiple "
                  "type 6 (document) records."),
-          handle_get_filename (h)));
+          handle_get_filename (r->fh)));
 
 
-  assertive_bufread (h, &n_lines, sizeof n_lines, 0);
-  if (n_lines <= 0)
+  assertive_buf_read (r, &line_cnt, sizeof line_cnt, 0);
+  if (line_cnt <= 0)
     lose ((ME, _("%s: Number of document lines (%ld) "
                  "must be greater than 0."),
     lose ((ME, _("%s: Number of document lines (%ld) "
                  "must be greater than 0."),
-          handle_get_filename (h), (long) n_lines));
+          handle_get_filename (r->fh), (long) line_cnt));
 
 
-  documents = bufread (h, NULL, 80 * n_lines, n_lines * 80 + 1);
+  documents = buf_read (r, NULL, 80 * line_cnt, line_cnt * 80 + 1);
   /* FIXME?  Run through asciify. */
   if (documents == NULL)
     return 0;
   /* FIXME?  Run through asciify. */
   if (documents == NULL)
     return 0;
-  documents[80 * n_lines] = '\0';
+  documents[80 * line_cnt] = '\0';
   dict_set_documents (dict, documents);
   free (documents);
   return 1;
 
   dict_set_documents (dict, documents);
   free (documents);
   return 1;
 
-lossage:
+error:
   return 0;
 }
 \f
   return 0;
 }
 \f
@@ -1183,43 +1139,40 @@ lossage:
    appropriately.  Returns nonzero only if both no errors occur and
    data was read. */
 static int
    appropriately.  Returns nonzero only if both no errors occur and
    data was read. */
 static int
-buffer_input (struct file_handle *h)
+buffer_input (struct sfm_reader *r)
 {
 {
-  struct sfm_fhuser_ext *ext = h->ext;
   size_t amt;
 
   size_t amt;
 
-  if (ext->buf == NULL)
-    ext->buf = xmalloc (sizeof *ext->buf * 128);
-  amt = fread (ext->buf, sizeof *ext->buf, 128, ext->file);
-  if (ferror (ext->file))
+  if (r->buf == NULL)
+    r->buf = xmalloc (sizeof *r->buf * 128);
+  amt = fread (r->buf, sizeof *r->buf, 128, r->file);
+  if (ferror (r->file))
     {
       msg (ME, _("%s: Error reading file: %s."),
     {
       msg (ME, _("%s: Error reading file: %s."),
-           handle_get_filename (h), strerror (errno));
+           handle_get_filename (r->fh), strerror (errno));
       return 0;
     }
       return 0;
     }
-  ext->ptr = ext->buf;
-  ext->end = &ext->buf[amt];
+  r->ptr = r->buf;
+  r->end = &r->buf[amt];
   return amt;
 }
 
   return amt;
 }
 
-/* Reads a single case consisting of compressed data from system file
-   H into the array TEMP[] according to dictionary DICT, and returns
-   nonzero only if successful. */
-/* Data in system files is compressed in the following manner:
-   data values are grouped into sets of eight; each of the eight has
-   one instruction byte, which are output together in an octet; each
-   byte gives a value for that byte or indicates that the value can be
-   found following the instructions. */
+/* Reads a single case consisting of compressed data from system
+   file H into the array BUF[] according to reader R, and
+   returns nonzero only if successful. */
+/* Data in system files is compressed in this manner.  Data
+   values are grouped into sets of eight ("octets").  Each value
+   in an octet has one instruction byte that are output together.
+   Each instruction byte gives a value for that byte or indicates
+   that the value can be found following the instructions. */
 static int
 static int
-read_compressed_data (struct file_handle *h, flt64 *temp)
+read_compressed_data (struct sfm_reader *r, flt64 *buf)
 {
 {
-  struct sfm_fhuser_ext *ext = h->ext;
-
-  const unsigned char *p_end = ext->x + sizeof (flt64);
-  unsigned char *p = ext->y;
+  const unsigned char *p_end = r->x + sizeof (flt64);
+  unsigned char *p = r->y;
 
 
-  const flt64 *temp_beg = temp;
-  const flt64 *temp_end = &temp[ext->case_size];
+  const flt64 *buf_beg = buf;
+  const flt64 *buf_end = &buf[r->value_cnt];
 
   for (;;)
     {
 
   for (;;)
     {
@@ -1231,152 +1184,181 @@ read_compressed_data (struct file_handle *h, flt64 *temp)
            continue;
          case 252:
            /* Code 252 is end of file. */
            continue;
          case 252:
            /* Code 252 is end of file. */
-           if (temp_beg != temp)
+           if (buf_beg != buf)
              lose ((ME, _("%s: Compressed data is corrupted.  Data ends "
                     "in partial case."),
              lose ((ME, _("%s: Compressed data is corrupted.  Data ends "
                     "in partial case."),
-                     handle_get_filename (h)));
-           goto lossage;
+                     handle_get_filename (r->fh)));
+           goto error;
          case 253:
            /* Code 253 indicates that the value is stored explicitly
               following the instruction bytes. */
          case 253:
            /* Code 253 indicates that the value is stored explicitly
               following the instruction bytes. */
-           if (ext->ptr == NULL || ext->ptr >= ext->end)
-             if (!buffer_input (h))
+           if (r->ptr == NULL || r->ptr >= r->end)
+             if (!buffer_input (r))
                {
                  lose ((ME, _("%s: Unexpected end of file."),
                {
                  lose ((ME, _("%s: Unexpected end of file."),
-                         handle_get_filename (h)));
-                 goto lossage;
+                         handle_get_filename (r->fh)));
+                 goto error;
                }
                }
-           memcpy (temp++, ext->ptr++, sizeof *temp);
-           if (temp >= temp_end)
-             goto winnage;
+           memcpy (buf++, r->ptr++, sizeof *buf);
+           if (buf >= buf_end)
+             goto success;
            break;
          case 254:
            /* Code 254 indicates a string that is all blanks. */
            break;
          case 254:
            /* Code 254 indicates a string that is all blanks. */
-           memset (temp++, ' ', sizeof *temp);
-           if (temp >= temp_end)
-             goto winnage;
+           memset (buf++, ' ', sizeof *buf);
+           if (buf >= buf_end)
+             goto success;
            break;
          case 255:
            /* Code 255 indicates the system-missing value. */
            break;
          case 255:
            /* Code 255 indicates the system-missing value. */
-           *temp = ext->sysmis;
-           if (ext->reverse_endian)
-             bswap_flt64 (temp);
-           temp++;
-           if (temp >= temp_end)
-             goto winnage;
+           *buf = r->sysmis;
+           if (r->reverse_endian)
+             bswap_flt64 (buf);
+           buf++;
+           if (buf >= buf_end)
+             goto success;
            break;
          default:
            /* Codes 1 through 251 inclusive are taken to indicate a
               value of (BYTE - BIAS), where BYTE is the byte's value
               and BIAS is the compression bias (generally 100.0). */
            break;
          default:
            /* Codes 1 through 251 inclusive are taken to indicate a
               value of (BYTE - BIAS), where BYTE is the byte's value
               and BIAS is the compression bias (generally 100.0). */
-           *temp = *p - ext->bias;
-           if (ext->reverse_endian)
-             bswap_flt64 (temp);
-           temp++;
-           if (temp >= temp_end)
-             goto winnage;
+           *buf = *p - r->bias;
+           if (r->reverse_endian)
+             bswap_flt64 (buf);
+           buf++;
+           if (buf >= buf_end)
+             goto success;
            break;
          }
 
       /* We have reached the end of this instruction octet.  Read
         another. */
            break;
          }
 
       /* We have reached the end of this instruction octet.  Read
         another. */
-      if (ext->ptr == NULL || ext->ptr >= ext->end)
-       if (!buffer_input (h))
+      if (r->ptr == NULL || r->ptr >= r->end)
+       if (!buffer_input (r))
          {
          {
-           if (temp_beg != temp)
+           if (buf_beg != buf)
              lose ((ME, _("%s: Unexpected end of file."),
              lose ((ME, _("%s: Unexpected end of file."),
-                     handle_get_filename (h)));
-           goto lossage;
+                     handle_get_filename (r->fh)));
+           goto error;
          }
          }
-      memcpy (ext->x, ext->ptr++, sizeof *temp);
-      p = ext->x;
+      memcpy (r->x, r->ptr++, sizeof *buf);
+      p = r->x;
     }
 
   /* Not reached. */
   assert (0);
 
     }
 
   /* Not reached. */
   assert (0);
 
-winnage:
+success:
   /* We have filled up an entire record.  Update state and return
      successfully. */
   /* We have filled up an entire record.  Update state and return
      successfully. */
-  ext->y = ++p;
+  r->y = ++p;
   return 1;
 
   return 1;
 
-lossage:
+error:
   /* We have been unsuccessful at filling a record, either through i/o
      error or through an end-of-file indication.  Update state and
      return unsuccessfully. */
   return 0;
 }
 
   /* We have been unsuccessful at filling a record, either through i/o
      error or through an end-of-file indication.  Update state and
      return unsuccessfully. */
   return 0;
 }
 
-/* Reads one case from system file H into PERM
-   according to the instructions given in associated dictionary DICT,
-   which must have the get.* elements appropriately set.  Returns
-   nonzero only if successful.  */
+/* Reads one case from READER's file into C.  Returns nonzero
+   only if successful. */
 int
 int
-sfm_read_case (struct file_handle *h, struct ccase *perm, struct dictionary *dict)
+sfm_read_case (struct sfm_reader *r, struct ccase *c)
 {
 {
-  struct sfm_fhuser_ext *ext = h->ext;
-
-  size_t nbytes;
-  flt64 *temp;
-
-  int i;
-
-  /* The first concern is to obtain a full case relative to the data
-     file.  (Cases in the data file have no particular relationship to
-     cases in the active file.) */
-  nbytes = sizeof *temp * ext->case_size;
-  temp = local_alloc (nbytes);
-
-  if (ext->compressed == 0)
+  if (!r->compressed && sizeof (flt64) == sizeof (double)) 
     {
     {
-      size_t amt = fread (temp, 1, nbytes, ext->file);
+      /* Fast path: external and internal representations are the
+         same, except possibly for endianness or SYSMIS.  Read
+         directly into the case's buffer, then fix up any minor
+         details as needed. */
+      if (!fread_ok (r, case_data_all_rw (c),
+                     sizeof (union value) * r->value_cnt))
+        return 0;
+
+      /* Fix up endianness if needed. */
+      if (r->reverse_endian) 
+        {
+          int i;
+          
+          for (i = 0; i < r->var_cnt; i++) 
+            if (r->vars[i].width == 0)
+              bswap_flt64 (&case_data_rw (c, r->vars[i].fv)->f);
+        }
 
 
-      if (amt != nbytes)
-       {
-         if (ferror (ext->file))
-           msg (ME, _("%s: Reading system file: %s."),
-                 handle_get_filename (h), strerror (errno));
-         else if (amt != 0)
-           msg (ME, _("%s: Partial record at end of system file."),
-                 handle_get_filename (h));
-         goto lossage;
-       }
+      /* Fix up SYSMIS values if needed.
+         I don't think this will ever actually kick in, but it
+         can't hurt. */
+      if (r->sysmis != SYSMIS) 
+        {
+          int i;
+          
+          for (i = 0; i < r->var_cnt; i++) 
+            if (r->vars[i].width == 0 && case_num (c, i) == r->sysmis)
+              case_data_rw (c, r->vars[i].fv)->f = SYSMIS;
+        }
     }
     }
-  else if (!read_compressed_data (h, temp))
-    goto lossage;
-
-  /* Translate a case in data file format to a case in active file
-     format. */
-  for (i = 0; i < dict_get_var_cnt (dict); i++)
+  else 
     {
     {
-      struct variable *v = dict_get_var (dict, i);
-
-      if (v->get.fv == -1)
-       continue;
-      
-      if (v->type == NUMERIC)
-       {
-         flt64 src = temp[v->get.fv];
-         if (ext->reverse_endian)
-           bswap_flt64 (&src);
-          case_data_rw (perm, v->fv)->f = src == ext->sysmis ? SYSMIS : src;
-       }
+      /* Slow path: internal and external representations differ.
+         Read into a bounce buffer, then copy to C. */
+      flt64 *bounce;
+      flt64 *bounce_cur;
+      size_t bounce_size;
+      int read_ok;
+      int i;
+
+      bounce_size = sizeof *bounce * r->value_cnt;
+      bounce = bounce_cur = local_alloc (bounce_size);
+
+      if (!r->compressed)
+        read_ok = fread_ok (r, bounce, bounce_size);
       else
       else
-       memcpy (case_data_rw (perm, v->fv)->s, &temp[v->get.fv], v->width);
-    }
+        read_ok = read_compressed_data (r, bounce);
+      if (!read_ok) 
+        {
+          local_free (bounce);
+          return 0;
+        }
 
 
-  local_free (temp);
-  return 1;
+      for (i = 0; i < r->var_cnt; i++)
+        {
+          struct sfm_var *v = &r->vars[i];
+
+          if (v->width == 0)
+            {
+              flt64 f = *bounce_cur++;
+              if (r->reverse_endian)
+                bswap_flt64 (&f);
+              case_data_rw (c, i)->f = f == r->sysmis ? SYSMIS : f;
+            }
+          else 
+            {
+              memcpy (case_data_rw (c, v->fv)->s, bounce_cur, v->width);
+              bounce_cur += DIV_RND_UP (v->width, sizeof (flt64));
+            }
+        }
 
 
-lossage:
-  local_free (temp);
-  return 0;
+      local_free (bounce);
+    }
+  return 1; 
 }
 
 }
 
-static struct fh_ext_class sfm_r_class =
+static int
+fread_ok (struct sfm_reader *r, void *buffer, size_t byte_cnt)
 {
 {
-  3,
-  N_("reading as a system file"),
-  sfm_close,
-};
+  size_t read_bytes = fread (buffer, 1, byte_cnt, r->file);
+
+  if (read_bytes == byte_cnt)
+    return 1;
+  else
+    {
+      if (ferror (r->file))
+        msg (ME, _("%s: Reading system file: %s."),
+             handle_get_filename (r->fh), strerror (errno));
+      else if (read_bytes != 0)
+        msg (ME, _("%s: Partial record at end of system file."),
+             handle_get_filename (r->fh));
+      return 0;
+    }
+}
diff --git a/src/sfm-read.h b/src/sfm-read.h
new file mode 100644 (file)
index 0000000..4c2e9f9
--- /dev/null
@@ -0,0 +1,45 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 1997-9, 2000 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., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#ifndef SFM_READ_H
+#define SFM_READ_H 1
+
+/* Reading system files. */
+
+/* System file info that doesn't fit in struct dictionary. */
+struct sfm_read_info
+  {
+    char creation_date[10];    /* `dd mmm yy' plus a null. */
+    char creation_time[9];     /* `hh:mm:ss' plus a null. */
+    int big_endian;            /* 1=big-endian, 0=little-endian. */
+    int compressed;            /* 0=no, 1=yes. */
+    int case_cnt;               /* -1 if unknown. */
+    char product[61];          /* Product name plus a null. */
+  };
+
+struct dictionary;
+struct file_handle;
+struct ccase;
+struct sfm_reader *sfm_open_reader (struct file_handle *,
+                                    struct dictionary **,
+                                    struct sfm_read_info *);
+int sfm_read_case (struct sfm_reader *, struct ccase *);
+void sfm_close_reader (struct sfm_reader *);
+
+#endif /* sfm-read.h */
index 8769e2358146cdddf367a1f63a0a93370fc03477..aedb5a8a4ad4d5ebe10b384912c10ccdb18d4603 100644 (file)
@@ -18,7 +18,7 @@
    02111-1307, USA. */
 
 #include <config.h>
    02111-1307, USA. */
 
 #include <config.h>
-#include "sfm.h"
+#include "sfm-write.h"
 #include "sfmP.h"
 #include "error.h"
 #include <stdlib.h>
 #include "sfmP.h"
 #include "error.h"
 #include <stdlib.h>
@@ -29,6 +29,8 @@
 #include <unistd.h>    /* Required by SunOS4. */
 #endif
 #include "alloc.h"
 #include <unistd.h>    /* Required by SunOS4. */
 #endif
 #include "alloc.h"
+#include "case.h"
+#include "dictionary.h"
 #include "error.h"
 #include "file-handle.h"
 #include "getline.h"
 #include "error.h"
 #include "file-handle.h"
 #include "getline.h"
 
 #include "debug-print.h"
 
 
 #include "debug-print.h"
 
-/* PORTME: This file may require substantial revision for those
-   systems that don't meet the typical 32-bit integer/64-bit double
-   model.  It's kinda hard to tell without having one of them on my
-   desk.  */
-
 /* Compression bias used by PSPP.  Values between (1 -
    COMPRESSION_BIAS) and (251 - COMPRESSION_BIAS) inclusive can be
    compressed. */
 #define COMPRESSION_BIAS 100
 
 /* Compression bias used by PSPP.  Values between (1 -
    COMPRESSION_BIAS) and (251 - COMPRESSION_BIAS) inclusive can be
    compressed. */
 #define COMPRESSION_BIAS 100
 
-/* sfm writer file_handle extension. */
-struct sfm_fhuser_ext
+/* System file writer. */
+struct sfm_writer
   {
   {
-    FILE *file;                        /* Actual file. */
+    struct file_handle *fh;     /* File handle. */
+    FILE *file;                        /* File stream. */
+
+    int needs_translation;      /* 0=use fast path, 1=translation needed. */
+    int compress;              /* 1=compressed, 0=not compressed. */
+    int case_cnt;              /* Number of cases written so far. */
+    size_t flt64_cnt;           /* Number of flt64 elements in case. */
 
 
-    int compressed;            /* 1=compressed, 0=not compressed. */
+    /* Compression buffering. */
     flt64 *buf;                        /* Buffered data. */
     flt64 *end;                        /* Buffer end. */
     flt64 *ptr;                        /* Current location in buffer. */
     unsigned char *x;          /* Location in current instruction octet. */
     unsigned char *y;          /* End of instruction octet. */
     flt64 *buf;                        /* Buffered data. */
     flt64 *end;                        /* Buffer end. */
     flt64 *ptr;                        /* Current location in buffer. */
     unsigned char *x;          /* Location in current instruction octet. */
     unsigned char *y;          /* End of instruction octet. */
-    int n_cases;               /* Number of cases written so far. */
 
 
-    char *elem_type;           /* ALPHA or NUMERIC for each flt64 element. */
+    /* Variables. */
+    struct sfm_var *vars;       /* Variables. */
+    size_t var_cnt;             /* Number of variables. */
   };
 
   };
 
-static struct fh_ext_class sfm_w_class;
+/* A variable in a system file. */
+struct sfm_var 
+  {
+    int width;                  /* 0=numeric, otherwise string width. */
+    int fv;                     /* Index into case. */
+    size_t flt64_cnt;           /* Number of flt64 elements. */
+  };
 
 static char *append_string_max (char *, const char *, const char *);
 
 static char *append_string_max (char *, const char *, const char *);
-static int write_header (struct sfm_write_info *inf);
-static int bufwrite (struct file_handle *h, const void *buf, size_t nbytes);
-static int write_variable (struct sfm_write_info *inf, struct variable *v);
-static int write_value_labels (struct sfm_write_info *inf, struct variable * s, int index);
-static int write_rec_7_34 (struct sfm_write_info *inf);
-static int write_documents (struct sfm_write_info *inf);
-
-/* Writes the dictionary INF->dict to system file INF->h.  The system
-   file is compressed if INF->compress is nonzero.  INF->case_size is
-   set to the number of flt64 elements in a single case.  Returns
-   nonzero only if successful. */
-int
-sfm_write_dictionary (struct sfm_write_info *inf)
+static int write_header (struct sfm_writer *, const struct dictionary *);
+static int buf_write (struct sfm_writer *, const void *, size_t);
+static int write_variable (struct sfm_writer *, struct variable *);
+static int write_value_labels (struct sfm_writer *,
+                               struct variable *, int idx);
+static int write_rec_7_34 (struct sfm_writer *);
+static int write_documents (struct sfm_writer *, const struct dictionary *);
+static int does_dict_need_translation (const struct dictionary *);
+
+static inline int
+var_flt64_cnt (const struct variable *v) 
+{
+  return v->type == NUMERIC ? 1 : DIV_RND_UP (v->width, sizeof (flt64));
+}
+
+/* Opens the system file designated by file handle FH for writing
+   cases from dictionary D.  If COMPRESS is nonzero, the
+   system file will be compressed.
+
+   No reference to D is retained, so it may be modified or
+   destroyed at will after this function returns. */
+struct sfm_writer *
+sfm_open_writer (struct file_handle *fh,
+                 const struct dictionary *d, int compress)
 {
 {
-  struct dictionary *d = inf->dict;
-  struct sfm_fhuser_ext *ext;
+  struct sfm_writer *w = NULL;
+  int idx;
   int i;
   int i;
-  int index;
 
 
-  if (inf->h->class != NULL)
+  if (!fh_open (fh, "system file", "we"))
+    goto error;
+
+  /* Create and initialize writer. */
+  w = xmalloc (sizeof *w);
+  w->fh = fh;
+  w->file = fopen (handle_get_filename (fh), "wb");
+
+  w->needs_translation = does_dict_need_translation (d);
+  w->compress = compress;
+  w->case_cnt = 0;
+  w->flt64_cnt = 0;
+
+  w->buf = w->end = w->ptr = NULL;
+  w->x = w->y = NULL;
+
+  w->var_cnt = dict_get_var_cnt (d);
+  w->vars = xmalloc (sizeof *w->vars * w->var_cnt);
+  for (i = 0; i < w->var_cnt; i++) 
     {
     {
-      msg (ME, _("Cannot write file %s as system file: "
-                 "already opened for %s."),
-          handle_get_name (inf->h), inf->h->class->name);
-      return 0;
+      const struct variable *dv = dict_get_var (d, i);
+      struct sfm_var *sv = &w->vars[i];
+      sv->width = dv->width;
+      sv->fv = dv->fv;
+      sv->flt64_cnt = var_flt64_cnt (dv);
     }
 
     }
 
-  msg (VM (1), _("%s: Opening system-file handle %s for writing."),
-       handle_get_filename (inf->h), handle_get_name (inf->h));
-  
-  /* Open the physical disk file. */
-  inf->h->class = &sfm_w_class;
-  inf->h->ext = ext = xmalloc (sizeof (struct sfm_fhuser_ext));
-  ext->file = fopen (handle_get_filename (inf->h), "wb");
-  ext->elem_type = NULL;
-  if (ext->file == NULL)
+  /* Check that file create succeeded. */
+  if (w->file == NULL)
     {
     {
-      msg (ME, _("An error occurred while opening \"%s\" for writing "
+      msg (ME, _("Error opening \"%s\" for writing "
                  "as a system file: %s."),
                  "as a system file: %s."),
-           handle_get_filename (inf->h), strerror (errno));
+           handle_get_filename (w->fh), strerror (errno));
       err_cond_fail ();
       err_cond_fail ();
-      free (ext);
-      return 0;
+      goto error;
     }
 
     }
 
-  /* Initialize the sfm_fhuser_ext structure. */
-  ext->compressed = inf->compress;
-  ext->buf = ext->ptr = NULL;
-  ext->x = ext->y = NULL;
-  ext->n_cases = 0;
-
   /* Write the file header. */
   /* Write the file header. */
-  if (!write_header (inf))
-    goto lossage;
+  if (!write_header (w, d))
+    goto error;
 
   /* Write basic variable info. */
   for (i = 0; i < dict_get_var_cnt (d); i++)
 
   /* Write basic variable info. */
   for (i = 0; i < dict_get_var_cnt (d); i++)
-    write_variable (inf, dict_get_var (d, i));
+    write_variable (w, dict_get_var (d, i));
 
   /* Write out value labels. */
 
   /* Write out value labels. */
-  for (index = i = 0; i < dict_get_var_cnt (d); i++)
+  for (idx = i = 0; i < dict_get_var_cnt (d); i++)
     {
       struct variable *v = dict_get_var (d, i);
 
     {
       struct variable *v = dict_get_var (d, i);
 
-      if (!write_value_labels (inf, v, index))
-       goto lossage;
-      index += (v->type == NUMERIC ? 1
-               : DIV_RND_UP (v->width, sizeof (flt64)));
+      if (!write_value_labels (w, v, idx))
+       goto error;
+      idx += var_flt64_cnt (v);
     }
 
     }
 
-  if (dict_get_documents (d) != NULL && !write_documents (inf))
-    goto lossage;
-  if (!write_rec_7_34 (inf))
-    goto lossage;
+  if (dict_get_documents (d) != NULL && !write_documents (w, d))
+    goto error;
+  if (!write_rec_7_34 (w))
+    goto error;
 
   /* Write record 999. */
   {
 
   /* Write record 999. */
   {
@@ -158,22 +182,41 @@ sfm_write_dictionary (struct sfm_write_info *inf)
     rec_999.rec_type = 999;
     rec_999.filler = 0;
 
     rec_999.rec_type = 999;
     rec_999.filler = 0;
 
-    if (!bufwrite (inf->h, &rec_999, sizeof rec_999))
-      goto lossage;
+    if (!buf_write (w, &rec_999, sizeof rec_999))
+      goto error;
   }
 
   }
 
-  msg (VM (2), _("Wrote system-file header successfully."));
+  if (w->compress) 
+    {
+      w->buf = xmalloc (sizeof *w->buf * 128);
+      w->ptr = w->buf;
+      w->end = &w->buf[128];
+      w->x = (unsigned char *) w->ptr++;
+      w->y = (unsigned char *) w->ptr;
+    }
   
   
-  return 1;
+  return w;
 
 
-lossage:
-  msg (VM (1), _("Error writing system-file header."));
-  fclose (ext->file);
-  inf->h->class = NULL;
-  inf->h->ext = NULL;
-  free (ext->elem_type);
-  ext->elem_type = NULL;
-  return 0;
+ error:
+  sfm_close_writer (w);
+  return NULL;
+}
+
+static int
+does_dict_need_translation (const struct dictionary *d)
+{
+  size_t case_idx;
+  size_t i;
+
+  case_idx = 0;
+  for (i = 0; i < dict_get_var_cnt (d); i++) 
+    {
+      struct variable *v = dict_get_var (d, i);
+      if (v->fv != case_idx)
+        return 0;
+      case_idx += v->nv;
+    }
+  return 1;
 }
 
 /* Returns value of X truncated to two least-significant digits. */
 }
 
 /* Returns value of X truncated to two least-significant digits. */
@@ -187,13 +230,10 @@ rerange (int x)
   return x;
 }
 
   return x;
 }
 
-/* Write the sysfile_header header to the system file represented by
-   INF. */
+/* Write the sysfile_header header to system file W. */
 static int
 static int
-write_header (struct sfm_write_info *inf)
+write_header (struct sfm_writer *w, const struct dictionary *d)
 {
 {
-  struct dictionary *d = inf->dict;
-  struct sfm_fhuser_ext *ext = inf->h->ext;
   struct sysfile_header hdr;
   char *p;
   int i;
   struct sysfile_header hdr;
   char *p;
   int i;
@@ -210,31 +250,17 @@ write_header (struct sfm_write_info *inf)
 
   hdr.layout_code = 2;
 
 
   hdr.layout_code = 2;
 
-  hdr.case_size = 0;
+  w->flt64_cnt = 0;
   for (i = 0; i < dict_get_var_cnt (d); i++)
   for (i = 0; i < dict_get_var_cnt (d); i++)
-    {
-      struct variable *v = dict_get_var (d, i);
-      hdr.case_size += (v->type == NUMERIC ? 1
-                       : DIV_RND_UP (v->width, sizeof (flt64)));
-    }
-  inf->case_size = hdr.case_size;
+    w->flt64_cnt += var_flt64_cnt (dict_get_var (d, i));
+  hdr.case_size = w->flt64_cnt;
 
 
-  p = ext->elem_type = xmalloc (inf->case_size);
-  for (i = 0; i < dict_get_var_cnt (d); i++)
-    {
-      struct variable *v = dict_get_var (d, i);
-      int count = (v->type == NUMERIC ? 1
-                   : DIV_RND_UP (v->width, sizeof (flt64)));
-      while (count--)
-        *p++ = v->type;
-    }
-
-  hdr.compressed = inf->compress;
+  hdr.compress = w->compress;
 
   if (dict_get_weight (d) != NULL)
     {
       struct variable *weight_var;
 
   if (dict_get_weight (d) != NULL)
     {
       struct variable *weight_var;
-      int recalc_weight_index = 1;
+      int recalc_weight_idx = 1;
       int i;
 
       weight_var = dict_get_weight (d);
       int i;
 
       weight_var = dict_get_weight (d);
@@ -243,18 +269,17 @@ write_header (struct sfm_write_info *inf)
          struct variable *v = dict_get_var (d, i);
           if (v == weight_var)
             break;
          struct variable *v = dict_get_var (d, i);
           if (v == weight_var)
             break;
-         recalc_weight_index += (v->type == NUMERIC ? 1
-                                 : DIV_RND_UP (v->width, sizeof (flt64)));
+         recalc_weight_idx += var_flt64_cnt (v);
        }
        }
-      hdr.weight_index = recalc_weight_index;
+      hdr.weight_idx = recalc_weight_idx;
     }
   else
     }
   else
-    hdr.weight_index = 0;
+    hdr.weight_idx = 0;
 
 
-  hdr.ncases = -1;
+  hdr.case_cnt = -1;
   hdr.bias = COMPRESSION_BIAS;
 
   hdr.bias = COMPRESSION_BIAS;
 
-  if ((time_t) - 1 == time (&t))
+  if (time (&t) == (time_t) -1)
     {
       memcpy (hdr.creation_date, "01 Jan 70", 9);
       memcpy (hdr.creation_time, "00:00:00", 8);
     {
       memcpy (hdr.creation_date, "01 Jan 70", 9);
       memcpy (hdr.creation_time, "00:00:00", 8);
@@ -262,10 +287,10 @@ write_header (struct sfm_write_info *inf)
   else
     {
       static const char *month_name[12] =
   else
     {
       static const char *month_name[12] =
-      {
-       "Jan", "Feb", "Mar", "Apr", "May", "Jun",
-       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
-      };
+        {
+          "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+          "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+        };
       struct tm *tmp = localtime (&t);
       int day = rerange (tmp->tm_mday);
       int mon = rerange (tmp->tm_mon + 1);
       struct tm *tmp = localtime (&t);
       int day = rerange (tmp->tm_mday);
       int mon = rerange (tmp->tm_mon + 1);
@@ -291,7 +316,7 @@ write_header (struct sfm_write_info *inf)
   
   memset (hdr.padding, 0, sizeof hdr.padding);
 
   
   memset (hdr.padding, 0, sizeof hdr.padding);
 
-  if (!bufwrite (inf->h, &hdr, sizeof hdr))
+  if (!buf_write (w, &hdr, sizeof hdr))
     return 0;
   return 1;
 }
     return 0;
   return 1;
 }
@@ -305,18 +330,18 @@ write_format_spec (struct fmt_spec *src, int32 *dest)
 }
 
 /* Write the variable record(s) for primary variable P and secondary
 }
 
 /* Write the variable record(s) for primary variable P and secondary
-   variable S to the system file represented by INF. */
+   variable S to system file W. */
 static int
 static int
-write_variable (struct sfm_write_info *inf, struct variable *v)
+write_variable (struct sfm_writer *w, struct variable *v)
 {
   struct sysfile_variable sv;
 
   /* Missing values. */
 {
   struct sysfile_variable sv;
 
   /* Missing values. */
-  flt64 m[3];                  /* Missing value values. */
-  int nm;                      /* Number of missing values, possibly negative. */
+  flt64 m[3];           /* Missing value values. */
+  int nm;               /* Number of missing values, possibly negative. */
 
   sv.rec_type = 2;
 
   sv.rec_type = 2;
-  sv.type = (v->type == NUMERIC ? 0 : v->width);
+  sv.type = v->width;
   sv.has_var_label = (v->label != NULL);
 
   switch (v->miss_type)
   sv.has_var_label = (v->label != NULL);
 
   switch (v->miss_type)
@@ -373,7 +398,7 @@ write_variable (struct sfm_write_info *inf, struct variable *v)
   write_format_spec (&v->write, &sv.write);
   memcpy (sv.name, v->name, strlen (v->name));
   memset (&sv.name[strlen (v->name)], ' ', 8 - strlen (v->name));
   write_format_spec (&v->write, &sv.write);
   memcpy (sv.name, v->name, strlen (v->name));
   memset (&sv.name[strlen (v->name)], ' ', 8 - strlen (v->name));
-  if (!bufwrite (inf->h, &sv, sizeof sv))
+  if (!buf_write (w, &sv, sizeof sv))
     return 0;
 
   if (v->label)
     return 0;
 
   if (v->label)
@@ -392,11 +417,11 @@ write_variable (struct sfm_write_info *inf, struct variable *v)
       memcpy (l.label, v->label, l.label_len);
       memset (&l.label[l.label_len], ' ', ext_len - l.label_len);
 
       memcpy (l.label, v->label, l.label_len);
       memset (&l.label[l.label_len], ' ', ext_len - l.label_len);
 
-      if (!bufwrite (inf->h, &l, offsetof (struct label, label) + ext_len))
-         return 0;
+      if (!buf_write (w, &l, offsetof (struct label, label) + ext_len))
+        return 0;
     }
 
     }
 
-  if (nm && !bufwrite (inf->h, m, sizeof *m * nm))
+  if (nm && !buf_write (w, m, sizeof *m * nm))
     return 0;
 
   if (v->type == ALPHA && v->width > (int) sizeof (flt64))
     return 0;
 
   if (v->type == ALPHA && v->width > (int) sizeof (flt64))
@@ -413,7 +438,7 @@ write_variable (struct sfm_write_info *inf, struct variable *v)
 
       pad_count = DIV_RND_UP (v->width, (int) sizeof (flt64)) - 1;
       for (i = 0; i < pad_count; i++)
 
       pad_count = DIV_RND_UP (v->width, (int) sizeof (flt64)) - 1;
       for (i = 0; i < pad_count; i++)
-       if (!bufwrite (inf->h, &sv, sizeof sv))
+       if (!buf_write (w, &sv, sizeof sv))
          return 0;
     }
 
          return 0;
     }
 
@@ -421,10 +446,10 @@ write_variable (struct sfm_write_info *inf, struct variable *v)
 }
 
 /* Writes the value labels for variable V having system file variable
 }
 
 /* Writes the value labels for variable V having system file variable
-   index INDEX to the system file associated with INF.  Returns
+   index IDX to system file W.  Returns
    nonzero only if successful. */
 static int
    nonzero only if successful. */
 static int
-write_value_labels (struct sfm_write_info * inf, struct variable *v, int index)
+write_value_labels (struct sfm_writer *w, struct variable *v, int idx)
 {
   struct value_label_rec
     {
 {
   struct value_label_rec
     {
@@ -433,7 +458,7 @@ write_value_labels (struct sfm_write_info * inf, struct variable *v, int index)
       flt64 labels[1] P;
     };
 
       flt64 labels[1] P;
     };
 
-  struct variable_index_rec
+  struct var_idx_rec
     {
       int32 rec_type P;
       int32 n_vars P;
     {
       int32 rec_type P;
       int32 n_vars P;
@@ -442,7 +467,7 @@ write_value_labels (struct sfm_write_info * inf, struct variable *v, int index)
 
   struct val_labs_iterator *i;
   struct value_label_rec *vlr;
 
   struct val_labs_iterator *i;
   struct value_label_rec *vlr;
-  struct variable_index_rec vir;
+  struct var_idx_rec vir;
   struct val_lab *vl;
   size_t vlr_size;
   flt64 *loc;
   struct val_lab *vl;
   size_t vlr_size;
   flt64 *loc;
@@ -475,7 +500,7 @@ write_value_labels (struct sfm_write_info * inf, struct variable *v, int index)
       loc += DIV_RND_UP (len + 1, sizeof (flt64));
     }
   
       loc += DIV_RND_UP (len + 1, sizeof (flt64));
     }
   
-  if (!bufwrite (inf->h, vlr, vlr_size))
+  if (!buf_write (w, vlr, vlr_size))
     {
       free (vlr);
       return 0;
     {
       free (vlr);
       return 0;
@@ -484,8 +509,8 @@ write_value_labels (struct sfm_write_info * inf, struct variable *v, int index)
 
   vir.rec_type = 4;
   vir.n_vars = 1;
 
   vir.rec_type = 4;
   vir.n_vars = 1;
-  vir.vars[0] = index + 1;
-  if (!bufwrite (inf->h, &vir, sizeof vir))
+  vir.vars[0] = idx + 1;
+  if (!buf_write (w, &vir, sizeof vir))
     return 0;
 
   return 1;
     return 0;
 
   return 1;
@@ -493,14 +518,13 @@ write_value_labels (struct sfm_write_info * inf, struct variable *v, int index)
 
 /* Writes record type 6, document record. */
 static int
 
 /* Writes record type 6, document record. */
 static int
-write_documents (struct sfm_write_info * inf)
+write_documents (struct sfm_writer *w, const struct dictionary *d)
 {
 {
-  struct dictionary *d = inf->dict;
   struct
   struct
-  {
-    int32 rec_type P;          /* Always 6. */
-    int32 n_lines P;           /* Number of lines of documents. */
-  }
+    {
+      int32 rec_type P;                /* Always 6. */
+      int32 n_lines P;         /* Number of lines of documents. */
+    }
   rec_6;
 
   const char *documents;
   rec_6;
 
   const char *documents;
@@ -511,9 +535,9 @@ write_documents (struct sfm_write_info * inf)
 
   rec_6.rec_type = 6;
   rec_6.n_lines = n_lines;
 
   rec_6.rec_type = 6;
   rec_6.n_lines = n_lines;
-  if (!bufwrite (inf->h, &rec_6, sizeof rec_6))
+  if (!buf_write (w, &rec_6, sizeof rec_6))
     return 0;
     return 0;
-  if (!bufwrite (inf->h, documents, 80 * n_lines))
+  if (!buf_write (w, documents, 80 * n_lines))
     return 0;
 
   return 1;
     return 0;
 
   return 1;
@@ -521,7 +545,7 @@ write_documents (struct sfm_write_info * inf)
 
 /* Writes record type 7, subtypes 3 and 4. */
 static int
 
 /* Writes record type 7, subtypes 3 and 4. */
 static int
-write_rec_7_34 (struct sfm_write_info * inf)
+write_rec_7_34 (struct sfm_writer *w)
 {
   struct
     {
 {
   struct
     {
@@ -588,7 +612,7 @@ write_rec_7_34 (struct sfm_write_info * inf)
   rec_7.elem_4[1] = FLT64_MAX;
   rec_7.elem_4[2] = second_lowest_flt64;
 
   rec_7.elem_4[1] = FLT64_MAX;
   rec_7.elem_4[2] = second_lowest_flt64;
 
-  if (!bufwrite (inf->h, &rec_7, sizeof rec_7))
+  if (!buf_write (w, &rec_7, sizeof rec_7))
     return 0;
   return 1;
 }
     return 0;
   return 1;
 }
@@ -596,15 +620,13 @@ write_rec_7_34 (struct sfm_write_info * inf)
 /* Write NBYTES starting at BUF to the system file represented by
    H. */
 static int
 /* Write NBYTES starting at BUF to the system file represented by
    H. */
 static int
-bufwrite (struct file_handle * h, const void *buf, size_t nbytes)
+buf_write (struct sfm_writer *w, const void *buf, size_t nbytes)
 {
 {
-  struct sfm_fhuser_ext *ext = h->ext;
-
-  assert (buf);
-  if (1 != fwrite (buf, nbytes, 1, ext->file))
+  assert (buf != NULL);
+  if (fwrite (buf, nbytes, 1, w->file) != 1)
     {
       msg (ME, _("%s: Writing system file: %s."),
     {
       msg (ME, _("%s: Writing system file: %s."),
-           handle_get_filename (h), strerror (errno));
+           handle_get_filename (w->fh), strerror (errno));
       return 0;
     }
   return 1;
       return 0;
     }
   return 1;
@@ -625,132 +647,169 @@ append_string_max (char *dest, const char *src, const char *end)
    element.  If there's not room, pads out the current instruction
    octet with zero and dumps out the buffer. */
 static inline int
    element.  If there's not room, pads out the current instruction
    octet with zero and dumps out the buffer. */
 static inline int
-ensure_buf_space (struct file_handle *h)
+ensure_buf_space (struct sfm_writer *w)
 {
 {
-  struct sfm_fhuser_ext *ext = h->ext;
-
-  if (ext->ptr >= ext->end)
+  if (w->ptr >= w->end)
     {
     {
-      memset (ext->x, 0, ext->y - ext->x);
-      ext->x = ext->y;
-      ext->ptr = ext->buf;
-      if (!bufwrite (h, ext->buf, sizeof *ext->buf * 128))
+      memset (w->x, 0, w->y - w->x);
+      w->x = w->y;
+      w->ptr = w->buf;
+      if (!buf_write (w, w->buf, sizeof *w->buf * 128))
        return 0;
     }
   return 1;
 }
 
        return 0;
     }
   return 1;
 }
 
-/* Writes case ELEM consisting of N_ELEM flt64 elements to the system
-   file represented by H.  Return success. */
+static void write_compressed_data (struct sfm_writer *w, const flt64 *elem);
+
+/* Writes case C to system file W.
+   Returns nonzero if successful. */
 int
 int
-sfm_write_case (struct file_handle * h, const flt64 *elem, int n_elem)
+sfm_write_case (struct sfm_writer *w, struct ccase *c)
 {
 {
-  struct sfm_fhuser_ext *ext = h->ext;
-  const flt64 *end_elem = &elem[n_elem];
-  char *elem_type = ext->elem_type;
+  w->case_cnt++;
 
 
-  ext->n_cases++;
+  if (!w->needs_translation && !w->compress
+      && sizeof (flt64) == sizeof (union value)) 
+    {
+      /* Fast path: external and internal representations are the
+         same and the dictionary is properly ordered.  Write
+         directly to file. */
+      buf_write (w, case_data_all (c), sizeof (union value) * w->flt64_cnt);
+    }
+  else 
+    {
+      /* Slow path: internal and external representations differ.
+         Write into a bounce buffer, then write to W. */
+      flt64 *bounce;
+      flt64 *bounce_cur;
+      size_t bounce_size;
+      size_t i;
+
+      bounce_size = sizeof *bounce * w->flt64_cnt;
+      bounce = bounce_cur = local_alloc (bounce_size);
+
+      for (i = 0; i < w->var_cnt; i++) 
+        {
+          struct sfm_var *v = &w->vars[i];
+
+          if (v->width == 0) 
+            *bounce_cur = case_num (c, v->fv);
+          else 
+            memcpy (bounce_cur, case_data (c, v->fv)->s, v->width);
+          bounce_cur += v->flt64_cnt;
+        }
 
 
-  if (ext->compressed == 0)
-    return bufwrite (h, elem, sizeof *elem * n_elem);
+      if (!w->compress)
+        buf_write (w, bounce, bounce_size);
+      else
+        write_compressed_data (w, bounce);
+
+      local_free (bounce); 
+    }
+  
+  return 1;
+}
 
 
-  if (ext->buf == NULL)
+static void
+put_instruction (struct sfm_writer *w, unsigned char instruction) 
+{
+  if (w->x >= w->y)
     {
     {
-      ext->buf = xmalloc (sizeof *ext->buf * 128);
-      ext->ptr = ext->buf;
-      ext->end = &ext->buf[128];
-      ext->x = (unsigned char *) (ext->ptr++);
-      ext->y = (unsigned char *) (ext->ptr);
+      if (!ensure_buf_space (w))
+        return;
+      w->x = (unsigned char *) w->ptr++;
+      w->y = (unsigned char *) w->ptr;
     }
     }
-  for (; elem < end_elem; elem++, elem_type++)
+  *w->x++ = instruction;
+}
+
+static void
+put_element (struct sfm_writer *w, const flt64 *elem) 
+{
+  if (!ensure_buf_space (w))
+    return;
+  memcpy (w->ptr++, elem, sizeof *elem);
+}
+
+static void
+write_compressed_data (struct sfm_writer *w, const flt64 *elem) 
+{
+  size_t i;
+
+  for (i = 0; i < w->var_cnt; i++)
     {
     {
-      if (ext->x >= ext->y)
-       {
-         if (!ensure_buf_space (h))
-           return 0;
-         ext->x = (unsigned char *) (ext->ptr++);
-         ext->y = (unsigned char *) (ext->ptr);
-       }
+      struct sfm_var *v = &w->vars[i];
 
 
-      if (*elem_type == NUMERIC)
+      if (v->width == 0) 
         {
         {
-         if (*elem == -FLT64_MAX)
+          if (*elem == -FLT64_MAX)
+            put_instruction (w, 255);
+          else if (*elem >= 1 - COMPRESSION_BIAS
+                   && *elem <= 251 - COMPRESSION_BIAS
+                   && *elem == (int) *elem) 
+            put_instruction (w, (int) *elem + COMPRESSION_BIAS);
+          else
             {
             {
-             *ext->x++ = 255;
-              continue;
-            }
-         else if (*elem > INT_MIN && *elem < INT_MAX)
-           {
-             int value = *elem;
-
-             if (value >= 1 - COMPRESSION_BIAS
-                 && value <= 251 - COMPRESSION_BIAS
-                 && value == *elem)
-                {
-                 *ext->x++ = value + COMPRESSION_BIAS;
-                  continue;
-                }
+              put_instruction (w, 253);
+              put_element (w, elem);
             }
             }
+          elem++;
         }
         }
-      else
-       {
-          if (0 == memcmp ((char *) elem,
-                          "                                           ",
-                          sizeof (flt64)))
+      else 
+        {
+          size_t j;
+          
+          for (j = 0; j < v->flt64_cnt; j++, elem++) 
             {
             {
-             *ext->x++ = 254;
-              continue;
+              if (!memcmp (elem, "        ", sizeof (flt64)))
+                put_instruction (w, 254);
+              else 
+                {
+                  put_instruction (w, 253);
+                  put_element (w, elem);
+                }
             }
         }
             }
         }
-      
-      *ext->x++ = 253;
-      if (!ensure_buf_space (h))
-       return 0;
-      *ext->ptr++ = *elem;
     }
     }
-
-  return 1;
 }
 
 /* Closes a system file after we're done with it. */
 }
 
 /* Closes a system file after we're done with it. */
-static void
-sfm_close (struct file_handle * h)
+void
+sfm_close_writer (struct sfm_writer *w)
 {
 {
-  struct sfm_fhuser_ext *ext = h->ext;
+  if (w == NULL)
+    return;
 
 
-  if (ext->buf != NULL && ext->ptr > ext->buf)
+  fh_close (w->fh, "system file", "we");
+  
+  if (w->file != NULL) 
     {
     {
-      memset (ext->x, 0, ext->y - ext->x);
-      bufwrite (h, ext->buf, (ext->ptr - ext->buf) * sizeof *ext->buf);
-    }
+      /* Flush buffer. */
+      if (w->buf != NULL && w->ptr > w->buf)
+        {
+          memset (w->x, 0, w->y - w->x);
+          buf_write (w, w->buf, (w->ptr - w->buf) * sizeof *w->buf);
+        }
 
 
-  /* Attempt to seek back to the beginning in order to write the
-     number of cases.  If that's not possible (i.e., we're writing to
-     a tty or a pipe), then it's not a big deal because we wrote the
-     code that indicates an unknown number of cases. */
-  if (0 == fseek (ext->file, offsetof (struct sysfile_header, ncases),
-                 SEEK_SET))
-    {
-      int32 n_cases = ext->n_cases;
+      /* Seek back to the beginning and update the number of cases.
+         This is just a courtesy to later readers, so there's no need
+         to check return values or report errors. */
+      if (!fseek (w->file, offsetof (struct sysfile_header, case_cnt), SEEK_SET))
+        {
+          int32 case_cnt = w->case_cnt;
 
 
-      /* I don't really care about the return value: it doesn't matter
-         whether this data is written.  This is the only situation in
-         which you will see me fail to check a return value. */
-      fwrite (&n_cases, sizeof n_cases, 1, ext->file);
-    }
+          /* I don't really care about the return value: it doesn't
+             matter whether this data is written. */
+          fwrite (&case_cnt, sizeof case_cnt, 1, w->file);
+        }
 
 
-  if (EOF == fclose (ext->file))
-    msg (ME, _("%s: Closing system file: %s."),
-         handle_get_filename (h), strerror (errno));
-  free (ext->buf);
+      if (fclose (w->file) == EOF)
+        msg (ME, _("%s: Closing system file: %s."),
+             handle_get_filename (w->fh), strerror (errno));
+    }
 
 
-  free (ext->elem_type);
-  free (ext);
+  free (w->buf);
+  free (w->vars);
+  free (w);
 }
 }
-
-static struct fh_ext_class sfm_w_class =
-{
-  4,
-  N_("writing as a system file"),
-  sfm_close,
-};
diff --git a/src/sfm-write.h b/src/sfm-write.h
new file mode 100644 (file)
index 0000000..82cf2c9
--- /dev/null
@@ -0,0 +1,33 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 1997-9, 2000 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., 59 Temple Place - Suite 330, Boston, MA
+   02111-1307, USA. */
+
+#ifndef SFM_WRITE_H
+#define SFM_WRITE_H 1
+
+/* Writing system files. */
+
+struct file_handle;
+struct dictionary;
+struct ccase;
+struct sfm_writer *sfm_open_writer (struct file_handle *,
+                                    const struct dictionary *, int compress);
+int sfm_write_case (struct sfm_writer *, struct ccase *);
+void sfm_close_writer (struct sfm_writer *);
+
+#endif /* sfm-write.h */
diff --git a/src/sfm.h b/src/sfm.h
deleted file mode 100644 (file)
index e569164..0000000
--- a/src/sfm.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 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., 59 Temple Place - Suite 330, Boston, MA
-   02111-1307, USA. */
-
-#if !sfm_h
-#define sfm_h 1
-
-/* System file manager (sfm).
-
-   This module is in charge of reading and writing system files.  For
-   now, only ordinary system files are supported; in the future, PC+
-   compatible system files should be supported, too.  sfm is an
-   fhuser, so see file-handle.h for the fhuser interface.  */
-
-/* Information produced by sfm_read_dictionary() that doesn't fit into
-   a dictionary struct. */
-struct sfm_read_info
-  {
-    char creation_date[10];    /* `dd mmm yy' plus a null. */
-    char creation_time[9];     /* `hh:mm:ss' plus a null. */
-    int bigendian;             /* 1=big-endian, 0=little-endian. */
-    int compressed;            /* 0=no, 1=yes. */
-    int ncases;                        /* -1 if unknown. */
-    char product[61];          /* Product name plus a null. */
-  };
-
-struct dictionary;
-struct file_handle;
-struct ccase;
-
-struct dictionary *sfm_read_dictionary (struct file_handle *,
-                                       struct sfm_read_info *);
-int sfm_read_case (struct file_handle *, struct ccase *, struct dictionary *);
-void sfm_maybe_close (struct file_handle *);
-
-/* Information needed by sfm_write_dictionary(). */
-struct sfm_write_info
-  {
-    /* Read by sfm_write_dictionary(). */
-    struct file_handle *h;     /* File handle. */
-    struct dictionary *dict;   /* Primary dictionary. */
-    int compress;              /* 1=compress, 0=do not compress. */
-
-    /* Written by sfm_write_dictionary(). */
-    int case_size;             /* Number of flt64 elements per case. */
-  };
-
-int sfm_write_dictionary (struct sfm_write_info *);
-int sfm_write_case (struct file_handle *, const flt64* elem, int n_elem);
-
-#endif /* !sfm_h */
index fd47a070e76882e9d13b22633ff0e339285ba01c..6734087e197ff935a73f4b7275fe07b886b7d9c4 100644 (file)
 /* Record Type 1: General Information. */
 struct sysfile_header
   {
 /* Record Type 1: General Information. */
 struct sysfile_header
   {
-    char rec_type[4] P;                /* Record-type code, "$FL2". */
-    char prod_name[60] P;      /* Product identification. */
-    int32 layout_code P;       /* 2. */
-    int32 case_size P;         /* Number of `value's per case. */
-    int32 compressed P;                /* 1=compressed, 0=not compressed. */
-    int32 weight_index P;      /* 1-based index of weighting var, or zero. */
-    int32 ncases P;            /* Number of cases, -1 if unknown. */
-    flt64 bias P;              /* Compression bias (100.0). */
-    char creation_date[9] P;   /* `dd mmm yy' creation date of file. */
-    char creation_time[8] P;   /* `hh:mm:ss' 24-hour creation time. */
-    char file_label[64] P;     /* File label. */
-    char padding[3] P;         /* Ignored padding. */
+    char rec_type[4] P;                /* 00: Record-type code, "$FL2". */
+    char prod_name[60] P;      /* 04: Product identification. */
+    int32 layout_code P;       /* 40: 2. */
+    int32 case_size P;         /* 44: Number of `value's per case. */
+    int32 compress P;          /* 48: 1=compressed, 0=not compressed. */
+    int32 weight_idx P;         /* 4c: 1-based index of weighting var, or 0. */
+    int32 case_cnt P;          /* 50: Number of cases, -1 if unknown. */
+    flt64 bias P;              /* 54: Compression bias (100.0). */
+    char creation_date[9] P;   /* 5c: `dd mmm yy' creation date of file. */
+    char creation_time[8] P;   /* 65: `hh:mm:ss' 24-hour creation time. */
+    char file_label[64] P;     /* 6d: File label. */
+    char padding[3] P;         /* ad: Ignored padding. */
   };
 
 /* Record Type 2: Variable. */
   };
 
 /* Record Type 2: Variable. */
index 6eba150b9928032db393430dbcd0c9083a4e0fda..733aa9fbd31c89f5548ebe4ea5c7b4b4a9649d01 100644 (file)
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include "alloc.h"
 #include "command.h"
 #include <stdlib.h>
 #include "alloc.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "lexer.h"
 #include "str.h"
 #include "error.h"
 #include "lexer.h"
 #include "str.h"
index 9d2d0b3c4023226cb08b57dcb9f4b222057b8347..265c2a230d53e0798cea632e1507d23f25e4cad5 100644 (file)
--- a/src/str.c
+++ b/src/str.c
@@ -326,6 +326,13 @@ ds_c_str (const struct string *st)
   return st->string;
 }
 
   return st->string;
 }
 
+/* Returns the string data inside ST. */
+char *
+ds_data (const struct string *st)
+{
+  return st->string;
+}
+
 /* Returns a pointer to the null terminator ST.
    This might not be an actual null character unless ds_c_str() has
    been called since the last modification to ST. */
 /* Returns a pointer to the null terminator ST.
    This might not be an actual null character unless ds_c_str() has
    been called since the last modification to ST. */
index b1a44e22c24b6f709b113444467e634107e5e49b..9b90cf60f93695e4e6715b2b0d82e782905f3ab4 100644 (file)
--- a/src/str.h
+++ b/src/str.h
@@ -40,7 +40,7 @@
   char *strtok_r (char *, const char *, char **);
 #endif
 
   char *strtok_r (char *, const char *, char **);
 #endif
 
-#if !HAVE_STPCPY && !__linux__
+#if !HAVE_STPCPY
   char *stpcpy (char *dest, const char *src);
 #endif
 
   char *stpcpy (char *dest, const char *src);
 #endif
 
index 5a91fc39b4e8d05b3bb9f3780dc5e84e11f014dd..5c8f6a651c2759c82f7965d1c114e117dfb918f0 100644 (file)
 #include "algorithm.h"
 #include "alloc.h"
 #include "command.h"
 #include "algorithm.h"
 #include "alloc.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "file-handle.h"
 #include "hash.h"
 #include "lexer.h"
 #include "misc.h"
 #include "output.h"
 #include "error.h"
 #include "file-handle.h"
 #include "hash.h"
 #include "lexer.h"
 #include "misc.h"
 #include "output.h"
-#include "sfm.h"
+#include "sfm-read.h"
 #include "som.h"
 #include "tab.h"
 #include "value-labels.h"
 #include "som.h"
 #include "tab.h"
 #include "value-labels.h"
@@ -73,21 +74,22 @@ cmd_sysfile_info (void)
   struct file_handle *h;
   struct dictionary *d;
   struct tab_table *t;
   struct file_handle *h;
   struct dictionary *d;
   struct tab_table *t;
-  struct sfm_read_info inf;
+  struct sfm_reader *reader;
+  struct sfm_read_info info;
   int r, nr;
   int i;
 
   lex_match_id ("FILE");
   lex_match ('=');
 
   int r, nr;
   int i;
 
   lex_match_id ("FILE");
   lex_match ('=');
 
-  h = fh_parse_file_handle ();
+  h = fh_parse ();
   if (!h)
     return CMD_FAILURE;
 
   if (!h)
     return CMD_FAILURE;
 
-  d = sfm_read_dictionary (h, &inf);
-  fh_close_handle (h);
-  if (!d)
+  reader = sfm_open_reader (h, &d, &info);
+  if (!reader)
     return CMD_FAILURE;
     return CMD_FAILURE;
+  sfm_close_reader (reader);
 
   t = tab_create (2, 9, 0);
   tab_vline (t, TAL_1 | TAL_SPACING, 1, 0, 8);
 
   t = tab_create (2, 9, 0);
   tab_vline (t, TAL_1 | TAL_SPACING, 1, 0, 8);
@@ -102,15 +104,15 @@ cmd_sysfile_info (void)
   }
   tab_text (t, 0, 2, TAB_LEFT, _("Created:"));
   tab_text (t, 1, 2, TAB_LEFT | TAT_PRINTF, "%s %s by %s",
   }
   tab_text (t, 0, 2, TAB_LEFT, _("Created:"));
   tab_text (t, 1, 2, TAB_LEFT | TAT_PRINTF, "%s %s by %s",
-               inf.creation_date, inf.creation_time, inf.product);
+               info.creation_date, info.creation_time, info.product);
   tab_text (t, 0, 3, TAB_LEFT, _("Endian:"));
   tab_text (t, 0, 3, TAB_LEFT, _("Endian:"));
-  tab_text (t, 1, 3, TAB_LEFT, inf.bigendian ? _("Big.") : _("Little."));
+  tab_text (t, 1, 3, TAB_LEFT, info.big_endian ? _("Big.") : _("Little."));
   tab_text (t, 0, 4, TAB_LEFT, _("Variables:"));
   tab_text (t, 1, 4, TAB_LEFT | TAT_PRINTF, "%d",
                dict_get_var_cnt (d));
   tab_text (t, 0, 5, TAB_LEFT, _("Cases:"));
   tab_text (t, 1, 5, TAB_LEFT | TAT_PRINTF,
   tab_text (t, 0, 4, TAB_LEFT, _("Variables:"));
   tab_text (t, 1, 4, TAB_LEFT | TAT_PRINTF, "%d",
                dict_get_var_cnt (d));
   tab_text (t, 0, 5, TAB_LEFT, _("Cases:"));
   tab_text (t, 1, 5, TAB_LEFT | TAT_PRINTF,
-               inf.ncases == -1 ? _("Unknown") : "%d", inf.ncases);
+               info.case_cnt == -1 ? _("Unknown") : "%d", info.case_cnt);
   tab_text (t, 0, 6, TAB_LEFT, _("Type:"));
   tab_text (t, 1, 6, TAB_LEFT, _("System File."));
   tab_text (t, 0, 7, TAB_LEFT, _("Weight:"));
   tab_text (t, 0, 6, TAB_LEFT, _("Type:"));
   tab_text (t, 1, 6, TAB_LEFT, _("System File."));
   tab_text (t, 0, 7, TAB_LEFT, _("Weight:"));
@@ -121,7 +123,7 @@ cmd_sysfile_info (void)
   }
   tab_text (t, 0, 8, TAB_LEFT, _("Mode:"));
   tab_text (t, 1, 8, TAB_LEFT | TAT_PRINTF,
   }
   tab_text (t, 0, 8, TAB_LEFT, _("Mode:"));
   tab_text (t, 1, 8, TAB_LEFT | TAT_PRINTF,
-               _("Compression %s."), inf.compressed ? _("on") : _("off"));
+               _("Compression %s."), info.compressed ? _("on") : _("off"));
   tab_dim (t, tab_natural_dimensions);
   tab_submit (t);
 
   tab_dim (t, tab_natural_dimensions);
   tab_submit (t);
 
index ab25065f2f3dc007394e3dddc13328f61b12101a..d5349dddf6f1a9b0431ef70c59266f9de0a757e5 100644 (file)
@@ -29,6 +29,7 @@
 #include "str.h"
 #include "case.h"
 #include "command.h"
 #include "str.h"
 #include "case.h"
 #include "command.h"
+#include "dictionary.h"
 #include "lexer.h"
 #include "error.h"
 #include "magic.h"
 #include "lexer.h"
 #include "error.h"
 #include "magic.h"
@@ -42,6 +43,7 @@
 #include "group_proc.h"
 #include "casefile.h"
 #include "levene.h"
 #include "group_proc.h"
 #include "casefile.h"
 #include "levene.h"
+/* (headers) */
 
 /* (specification)
    "T-TEST" (tts_):
 
 /* (specification)
    "T-TEST" (tts_):
@@ -342,7 +344,7 @@ cmd_t_test(void)
       /* Destroy any group statistics we created */
       for (v = 0 ; v < cmd.n_variables ; ++v ) 
        {
       /* Destroy any group statistics we created */
       for (v = 0 ; v < cmd.n_variables ; ++v ) 
        {
-         struct group_proc *grpp = &cmd.v_variables[v]->p.grp_data;
+         struct group_proc *grpp = group_proc_get (cmd.v_variables[v]);
          free(grpp->group_hash);
        }
     }
          free(grpp->group_hash);
        }
     }
@@ -749,7 +751,7 @@ ssbox_independent_samples_populate(struct ssbox *ssb,
   for (i=0; i < cmd->n_variables; ++i)
     {
       struct variable *var = cmd->v_variables[i];
   for (i=0; i < cmd->n_variables; ++i)
     {
       struct variable *var = cmd->v_variables[i];
-      struct hsh_table *grp_hash = var->p.grp_data.group_hash;
+      struct hsh_table *grp_hash = group_proc_get (var)->group_hash;
       int count=0;
 
       tab_text (ssb->t, 0, i*2+1, TAB_LEFT, cmd->v_variables[i]->name);
       int count=0;
 
       tab_text (ssb->t, 0, i*2+1, TAB_LEFT, cmd->v_variables[i]->name);
@@ -849,7 +851,7 @@ ssbox_paired_populate(struct ssbox *ssb,struct cmd_t_test *cmd UNUSED)
        {
          struct group_statistics *gs;
 
        {
          struct group_statistics *gs;
 
-         gs=&pairs[i].v[j]->p.grp_data.ugs;
+         gs = &group_proc_get (pairs[i].v[j])->ugs;
 
          /* Titles */
 
 
          /* Titles */
 
@@ -875,8 +877,7 @@ ssbox_one_sample_populate(struct ssbox *ssb, struct cmd_t_test *cmd)
 
   for (i=0; i < cmd->n_variables; ++i)
     {
 
   for (i=0; i < cmd->n_variables; ++i)
     {
-      struct group_statistics *gs;
-      gs= &cmd->v_variables[i]->p.grp_data.ugs;
+      struct group_statistics *gs = &group_proc_get (cmd->v_variables[i])->ugs;
 
       tab_text (ssb->t, 0, i+1, TAB_LEFT, cmd->v_variables[i]->name);
       tab_float (ssb->t,1, i+1, TAB_RIGHT, gs->n, 2, 0);
 
       tab_text (ssb->t, 0, i+1, TAB_LEFT, cmd->v_variables[i]->name);
       tab_float (ssb->t,1, i+1, TAB_RIGHT, gs->n, 2, 0);
@@ -1011,8 +1012,9 @@ trbox_independent_samples_populate(struct trbox *self,
       double mean_diff;
 
       struct variable *var = cmd->v_variables[i];
       double mean_diff;
 
       struct variable *var = cmd->v_variables[i];
+      struct group_proc *grp_data = group_proc_get (var);
 
 
-      struct hsh_table *grp_hash = var->p.grp_data.group_hash;
+      struct hsh_table *grp_hash = grp_data->group_hash;
 
       struct group_statistics *gs0 ;
       struct group_statistics *gs1 ;
 
       struct group_statistics *gs0 ;
       struct group_statistics *gs1 ;
@@ -1041,12 +1043,11 @@ trbox_independent_samples_populate(struct trbox *self,
       tab_text (self->t, 1, i*2+3, TAB_LEFT, _("Equal variances assumed"));
 
 
       tab_text (self->t, 1, i*2+3, TAB_LEFT, _("Equal variances assumed"));
 
 
-      tab_float(self->t, 2, i*2+3, TAB_CENTER, 
-               cmd->v_variables[i]->p.grp_data.levene, 8,3);
+      tab_float(self->t, 2, i*2+3, TAB_CENTER, grp_data->levene, 8,3);
 
       /* Now work out the significance of the Levene test */
 
       /* Now work out the significance of the Levene test */
-      df1 = 1; df2 = cmd->v_variables[i]->p.grp_data.ugs.n - 2;
-      q = gsl_cdf_fdist_Q(cmd->v_variables[i]->p.grp_data.levene, df1, df2);
+      df1 = 1; df2 = grp_data->ugs.n - 2;
+      q = gsl_cdf_fdist_Q(grp_data->levene, df1, df2);
 
       tab_float(self->t, 3, i*2+3, TAB_CENTER, q, 8,3 );
 
 
       tab_float(self->t, 3, i*2+3, TAB_CENTER, q, 8,3 );
 
@@ -1282,8 +1283,7 @@ trbox_one_sample_populate(struct trbox *trb, struct cmd_t_test *cmd)
       double t;
       double p,q;
       double df;
       double t;
       double p,q;
       double df;
-      struct group_statistics *gs;
-      gs= &cmd->v_variables[i]->p.grp_data.ugs;
+      struct group_statistics *gs = &group_proc_get (cmd->v_variables[i])->ugs;
 
 
       tab_text (trb->t, 0, i+3, TAB_LEFT, cmd->v_variables[i]->name);
 
 
       tab_text (trb->t, 0, i+3, TAB_LEFT, cmd->v_variables[i]->name);
@@ -1445,7 +1445,7 @@ common_calc (const struct ccase *c, void *_cmd)
       struct variable *v = cmd->v_variables[i];
       const union value *val = case_data (c, v->fv);
 
       struct variable *v = cmd->v_variables[i];
       const union value *val = case_data (c, v->fv);
 
-      gs= &cmd->v_variables[i]->p.grp_data.ugs;
+      gs= &group_proc_get (cmd->v_variables[i])->ugs;
 
       if (! value_is_missing(val,v) )
        {
 
       if (! value_is_missing(val,v) )
        {
@@ -1466,7 +1466,7 @@ common_precalc ( struct cmd_t_test *cmd )
   for(i=0; i< cmd->n_variables ; ++i) 
     {
       struct group_statistics *gs;
   for(i=0; i< cmd->n_variables ; ++i) 
     {
       struct group_statistics *gs;
-      gs= &cmd->v_variables[i]->p.grp_data.ugs;
+      gs= &group_proc_get (cmd->v_variables[i])->ugs;
       
       gs->sum=0;
       gs->n=0;
       
       gs->sum=0;
       gs->n=0;
@@ -1485,7 +1485,7 @@ common_postcalc (  struct cmd_t_test *cmd )
   for(i=0; i< cmd->n_variables ; ++i) 
     {
       struct group_statistics *gs;
   for(i=0; i< cmd->n_variables ; ++i) 
     {
       struct group_statistics *gs;
-      gs= &cmd->v_variables[i]->p.grp_data.ugs;
+      gs= &group_proc_get (cmd->v_variables[i])->ugs;
       
       gs->mean=gs->sum / gs->n;
       gs->s_std_dev= sqrt(
       
       gs->mean=gs->sum / gs->n;
       gs->s_std_dev= sqrt(
@@ -1533,7 +1533,7 @@ one_sample_calc (const struct ccase *c, void *cmd_)
       struct variable *v = cmd->v_variables[i];
       const union value *val = case_data (c, v->fv);
 
       struct variable *v = cmd->v_variables[i];
       const union value *val = case_data (c, v->fv);
 
-      gs= &cmd->v_variables[i]->p.grp_data.ugs;
+      gs= &group_proc_get (cmd->v_variables[i])->ugs;
       
       if ( ! value_is_missing(val,v))
        gs->sum_diff += weight * (val->f - cmd->n_testval[0]);
       
       if ( ! value_is_missing(val,v))
        gs->sum_diff += weight * (val->f - cmd->n_testval[0]);
@@ -1551,7 +1551,7 @@ one_sample_precalc ( struct cmd_t_test *cmd )
   for(i=0; i< cmd->n_variables ; ++i) 
     {
       struct group_statistics *gs;
   for(i=0; i< cmd->n_variables ; ++i) 
     {
       struct group_statistics *gs;
-      gs= &cmd->v_variables[i]->p.grp_data.ugs;
+      gs= &group_proc_get (cmd->v_variables[i])->ugs;
       
       gs->sum_diff=0;
     }
       
       gs->sum_diff=0;
     }
@@ -1566,7 +1566,7 @@ one_sample_postcalc (struct cmd_t_test *cmd)
   for(i=0; i< cmd->n_variables ; ++i) 
     {
       struct group_statistics *gs;
   for(i=0; i< cmd->n_variables ; ++i) 
     {
       struct group_statistics *gs;
-      gs= &cmd->v_variables[i]->p.grp_data.ugs;
+      gs= &group_proc_get (cmd->v_variables[i])->ugs;
 
       gs->mean_diff = gs->sum_diff / gs->n ;
     }
 
       gs->mean_diff = gs->sum_diff / gs->n ;
     }
@@ -1715,7 +1715,7 @@ group_precalc (struct cmd_t_test *cmd )
 
   for(i=0; i< cmd->n_variables ; ++i) 
     {
 
   for(i=0; i< cmd->n_variables ; ++i) 
     {
-      struct group_proc *ttpr = &cmd->v_variables[i]->p.grp_data;
+      struct group_proc *ttpr = group_proc_get (cmd->v_variables[i]);
 
       /* There's always 2 groups for a T - TEST */
       ttpr->n_groups = 2;
 
       /* There's always 2 groups for a T - TEST */
       ttpr->n_groups = 2;
@@ -1791,7 +1791,7 @@ group_calc (const struct ccase *c, struct cmd_t_test *cmd)
     {
       struct variable *var = cmd->v_variables[i];
       const union value *val = case_data (c, var->fv);
     {
       struct variable *var = cmd->v_variables[i];
       const union value *val = case_data (c, var->fv);
-      struct hsh_table *grp_hash = var->p.grp_data.group_hash;
+      struct hsh_table *grp_hash = group_proc_get (var)->group_hash;
       struct group_statistics *gs;
 
       gs = hsh_find(grp_hash, (void *) gv);
       struct group_statistics *gs;
 
       gs = hsh_find(grp_hash, (void *) gv);
@@ -1821,7 +1821,7 @@ group_postcalc ( struct cmd_t_test *cmd )
   for(i=0; i< cmd->n_variables ; ++i) 
     {
       struct variable *var = cmd->v_variables[i];
   for(i=0; i< cmd->n_variables ; ++i) 
     {
       struct variable *var = cmd->v_variables[i];
-      struct hsh_table *grp_hash = var->p.grp_data.group_hash;
+      struct hsh_table *grp_hash = group_proc_get (var)->group_hash;
       struct hsh_iterator g;
       struct group_statistics *gs;
       int count=0;
       struct hsh_iterator g;
       struct group_statistics *gs;
       int count=0;
index c04ce764d99b18a99274fe2adfa9503f9f5854b7..0edb37f5ff772a3a7f48ff8b5147755e6b1933f8 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include "alloc.h"
 #include "command.h"
 #include <stdlib.h>
 #include "alloc.h"
 #include "command.h"
+#include "dictionary.h"
 #include "do-ifP.h"
 #include "error.h"
 #include "hash.h"
 #include "do-ifP.h"
 #include "error.h"
 #include "hash.h"
index e0191acc8bdf3880421e76c34304c0ee2f93856f..68a38e47f084f3e9d96e27bb8f0b0a8f943d0153 100644 (file)
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include "alloc.h"
 #include "command.h"
 #include <stdlib.h>
 #include "alloc.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "lexer.h"
 #include "main.h"
 #include "error.h"
 #include "lexer.h"
 #include "main.h"
index 91dd1702e1f69beaac15933077d8b27beb90c3e7..8b1d32869e2622398111f5bc2afcb9024ddbec80 100644 (file)
@@ -21,9 +21,9 @@
 #include "value-labels.h"
 #include "error.h"
 #include <stdlib.h>
 #include "value-labels.h"
 #include "error.h"
 #include <stdlib.h>
-#include <string.h>
 #include "alloc.h"
 #include "hash.h"
 #include "alloc.h"
 #include "hash.h"
+#include "str.h"
 
 static hsh_compare_func compare_int_val_lab;
 static hsh_hash_func hash_int_val_lab;
 
 static hsh_compare_func compare_int_val_lab;
 static hsh_hash_func hash_int_val_lab;
index d7756fce2830ffb134963f9ef51e4cf91f5fcd15..46a0efc5fc0a061d83f315ec9f6cacb93741efa9 100644 (file)
--- a/src/var.h
+++ b/src/var.h
@@ -22,7 +22,6 @@
 
 #include <stddef.h>
 #include "format.h"
 
 #include <stddef.h>
 #include "format.h"
-#include "group_proc.h"
 #include "val.h"
 
 /* Frequency tables. */
 #include "val.h"
 
 /* Frequency tables. */
@@ -67,93 +66,6 @@ struct freq_tab
     double valid_cases;                /* Sum of weights of valid cases. */
   };
 \f
     double valid_cases;                /* Sum of weights of valid cases. */
   };
 \f
-/* Procedures' private per-variable data. */
-
-/* Structure name suffixes for private data:
-   _proc: for a procedure (i.e., LIST -> list_proc).
-   _trns: for a transformation (i.e., COMPUTE -> compute_trns.
-   _pgm: for an input program (i.e., DATA LIST -> data_list_pgm). */
-
-/* CROSSTABS private data. */
-struct crosstab_proc
-  {
-    /* Integer mode only. */
-    int min;                   /* Minimum value. */
-    int max;                   /* Maximum value + 1. */
-    int count;                 /* max - min. */
-  };
-
-
-/* FREQUENCIES private data. */
-enum
-  {
-    frq_mean = 0, frq_semean, frq_median, frq_mode, frq_stddev, frq_variance,
-    frq_kurt, frq_sekurt, frq_skew, frq_seskew, frq_range, frq_min, frq_max,
-    frq_sum, frq_n_stats
-  };
-
-struct frequencies_proc
-  {
-    int used;                   /* 1=This variable already used. */
-
-    /* Freqency table. */
-    struct freq_tab tab;       /* Frequencies table to use. */
-
-    /* Percentiles. */
-    int n_groups;              /* Number of groups. */
-    double *groups;            /* Groups. */
-
-    /* Statistics. */
-    double stat[frq_n_stats];
-  };
-
-/* LIST private data. */
-struct list_proc
-  {
-    int newline;               /* Whether a new line begins here. */
-    int width;                 /* Field width. */
-    int vert;                  /* Whether to print the varname vertically. */
-  };
-
-/* GET private data. */
-struct get_proc
-  {
-    int fv, nv;                        /* First, # of values. */
-  };
-
-/* MEANS private data. */
-struct means_proc
-  {
-    double min, max;           /* Range for integer mode. */
-  };
-
-/* Different types of variables for MATRIX DATA procedure.  Order is
-   important: these are used for sort keys. */
-enum
-  {
-    MXD_SPLIT,                 /* SPLIT FILE variables. */
-    MXD_ROWTYPE,               /* ROWTYPE_. */
-    MXD_FACTOR,                        /* Factor variables. */
-    MXD_VARNAME,               /* VARNAME_. */
-    MXD_CONTINUOUS,            /* Continuous variables. */
-
-    MXD_COUNT
-  };
-
-/* MATRIX DATA private data. */
-struct matrix_data_proc
-  {
-    int vartype;               /* Variable type. */
-    int subtype;               /* Subtype. */
-  };
-
-/* MATCH FILES private data. */
-struct match_files_proc
-  {
-    struct variable *master;   /* Corresponding master file variable. */
-  };
-
-\f
 /* Script variables. */
 
 /* Variable type. */
 /* Script variables. */
 
 /* Variable type. */
@@ -164,8 +76,7 @@ enum
   };
 
 /* Types of missing values.  Order is significant, see
   };
 
 /* Types of missing values.  Order is significant, see
-   mis-val.c:parse_numeric(), sfm-read.c:sfm_read_dictionary()
-   sfm-write.c:sfm_write_dictionary(),
+   mis-val.c:parse_numeric(), sfm-read.c, sfm-write.c,
    sysfile-info.c:cmd_sysfile_info(), mis-val.c:copy_missing_values(),
    pfm-read.c:read_variables(), pfm-write.c:write_variables(),
    apply-dict.c:cmd_apply_dictionary(), and more (?). */
    sysfile-info.c:cmd_sysfile_info(), mis-val.c:copy_missing_values(),
    pfm-read.c:read_variables(), pfm-write.c:write_variables(),
    apply-dict.c:cmd_apply_dictionary(), and more (?). */
@@ -208,25 +119,20 @@ struct variable
     struct val_labs *val_labs;  /* Value labels. */
     char *label;               /* Variable label. */
 
     struct val_labs *val_labs;  /* Value labels. */
     char *label;               /* Variable label. */
 
-    /* Per-procedure info. */
+    /* Per-command info. */
     void *aux;
     void *aux;
-    struct get_proc get;
-    union
-      {
-       struct crosstab_proc crs;
-       struct frequencies_proc frq;
-       struct list_proc lst;
-       struct means_proc mns;
-       struct matrix_data_proc mxd;
-       struct match_files_proc mtf;
-       struct group_proc grp_data;
-      }
-    p;
+    void (*aux_dtor) (struct variable *);
   };
 
 int compare_variables (const void *, const void *, void *);
 unsigned hash_variable (const void *, void *);
 
   };
 
 int compare_variables (const void *, const void *, void *);
 unsigned hash_variable (const void *, void *);
 
+void *var_attach_aux (struct variable *,
+                      void *aux, void (*aux_dtor) (struct variable *));
+void var_clear_aux (struct variable *);
+void *var_detach_aux (struct variable *);
+void var_dtor_free (struct variable *);
+
 /* Classes of variables. */
 enum dict_class 
   {
 /* Classes of variables. */
 enum dict_class 
   {
@@ -247,82 +153,6 @@ struct vector
     int cnt;                   /* Number of variables. */
   };
 \f
     int cnt;                   /* Number of variables. */
   };
 \f
-/* Dictionary. */ 
-
-/* Complete dictionary state. */
-struct dictionary;
-
-struct dictionary *dict_create (void);
-struct dictionary *dict_clone (const struct dictionary *);
-void dict_clear (struct dictionary *);
-void dict_destroy (struct dictionary *);
-
-size_t dict_get_var_cnt (const struct dictionary *);
-struct variable *dict_get_var (const struct dictionary *, size_t idx);
-void dict_get_vars (const struct dictionary *,
-                    struct variable ***vars, size_t *cnt,
-                    unsigned exclude_classes);
-
-struct variable *dict_create_var (struct dictionary *, const char *,
-                                  int width);
-struct variable *dict_create_var_assert (struct dictionary *, const char *,
-                                  int width);
-struct variable *dict_clone_var (struct dictionary *, const struct variable *,
-                                 const char *);
-void dict_rename_var (struct dictionary *, struct variable *, const char *);
-
-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 *);
-void dict_delete_var (struct dictionary *, struct variable *);
-void dict_delete_vars (struct dictionary *,
-                       struct variable *const *, size_t count);
-void dict_reorder_vars (struct dictionary *,
-                        struct variable *const *, size_t count);
-int 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 *);
-double dict_get_case_weight (const struct dictionary *, 
-                            const struct ccase *, int *);
-void dict_set_weight (struct dictionary *, struct variable *);
-
-struct variable *dict_get_filter (const struct dictionary *);
-void dict_set_filter (struct dictionary *, struct variable *);
-
-int dict_get_case_limit (const struct dictionary *);
-void dict_set_case_limit (struct dictionary *, int);
-
-int dict_get_next_value_idx (const struct dictionary *);
-size_t dict_get_case_size (const struct dictionary *);
-
-void dict_compact_values (struct dictionary *);
-size_t dict_get_compacted_value_cnt (const struct dictionary *);
-int *dict_get_compacted_idx_to_fv (const struct dictionary *);
-
-struct variable *const *dict_get_split_vars (const struct dictionary *);
-size_t dict_get_split_cnt (const struct dictionary *);
-void dict_set_split_vars (struct dictionary *,
-                          struct variable *const *, size_t cnt);
-
-const char *dict_get_label (const struct dictionary *);
-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);
-const struct vector *dict_get_vector (const struct dictionary *,
-                                      size_t idx);
-size_t dict_get_vector_cnt (const struct dictionary *);
-const struct vector *dict_lookup_vector (const struct dictionary *,
-                                         const char *name);
-void dict_clear_vectors (struct dictionary *);
 \f
 void discard_variables (void);
 
 \f
 void discard_variables (void);
 
@@ -360,6 +190,7 @@ void cancel_temporary (void);
 \f
 /* Functions. */
 
 \f
 /* Functions. */
 
+struct ccase;
 void dump_split_vars (const struct ccase *);
 typedef int (* is_missing_func )(const union value *, const struct variable *);
 
 void dump_split_vars (const struct ccase *);
 typedef int (* is_missing_func )(const union value *, const struct variable *);
 
index 0b6877dbf72c1012d6c2d7ffa44a5e3d654689ef..3ce08b05ce753c69eb841c53482d5dc90f8dde68 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include "alloc.h"
 #include "command.h"
 #include <stdlib.h>
 #include "alloc.h"
 #include "command.h"
+#include "dictionary.h"
 #include "do-ifP.h"
 #include "expr.h"
 #include "file-handle.h"
 #include "do-ifP.h"
 #include "expr.h"
 #include "file-handle.h"
 
 #include "debug-print.h"
 
 
 #include "debug-print.h"
 
+void *
+var_attach_aux (struct variable *v,
+                void *aux, void (*aux_dtor) (struct variable *)) 
+{
+  assert (v->aux == NULL);
+  assert (aux != NULL);
+  v->aux = aux;
+  v->aux_dtor = aux_dtor;
+  return aux;
+}
+
+void *
+var_detach_aux (struct variable *v) 
+{
+  void *aux = v->aux;
+  assert (aux != NULL);
+  v->aux = NULL;
+  return aux;
+}
+
+void
+var_clear_aux (struct variable *v) 
+{
+  assert (v != NULL);
+  if (v->aux != NULL) 
+    {
+      if (v->aux_dtor != NULL)
+        v->aux_dtor (v);
+      v->aux = NULL;
+    }
+}
+
+void
+var_dtor_free (struct variable *v) 
+{
+  free (v->aux);
+}
+
 /* Compares A and B, which both have the given WIDTH, and returns
    a strcmp()-type result. */
 int
 /* Compares A and B, which both have the given WIDTH, and returns
    a strcmp()-type result. */
 int
@@ -67,7 +106,7 @@ void
 discard_variables (void)
 {
   dict_clear (default_dict);
 discard_variables (void)
 {
   dict_clear (default_dict);
-  default_handle = inline_file;
+  default_handle = NULL;
 
   n_lag = 0;
   
 
   n_lag = 0;
   
index 91376a5a68eccd08c74270fa0d1e10b189a65679..3a83964facbdcb378e6246a1366e3ab82a1b9a98 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include "alloc.h"
 #include "bitvector.h"
 #include <stdlib.h>
 #include "alloc.h"
 #include "bitvector.h"
+#include "dictionary.h"
 #include "error.h"
 #include "hash.h"
 #include "lexer.h"
 #include "error.h"
 #include "hash.h"
 #include "lexer.h"
index 80582f76b3db459e34342af957e2a6ba35b446d8..2a712a6220c310e345433ac73e81ef8c8f7ef336 100644 (file)
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include "alloc.h"
 #include "command.h"
 #include <stdlib.h>
 #include "alloc.h"
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "lexer.h"
 #include "misc.h"
 #include "error.h"
 #include "lexer.h"
 #include "misc.h"
index 1d3d2a5ca627a2d98db8f65b124c46e3eeaea1af..89624165209e611dc13deb8215b94bcee16490e7 100644 (file)
--- a/src/vfm.c
+++ b/src/vfm.c
@@ -30,6 +30,7 @@
 #include "alloc.h"
 #include "case.h"
 #include "casefile.h"
 #include "alloc.h"
 #include "case.h"
 #include "casefile.h"
+#include "dictionary.h"
 #include "do-ifP.h"
 #include "error.h"
 #include "expr.h"
 #include "do-ifP.h"
 #include "error.h"
 #include "expr.h"
index 7fac10cca7f4af6f193e38bdf616221c350e3300..c95b7910b28ff5d81365074ea0f800d2a6746354 100644 (file)
@@ -21,6 +21,7 @@
 #include "error.h"
 #include <stdio.h>
 #include "command.h"
 #include "error.h"
 #include <stdio.h>
 #include "command.h"
+#include "dictionary.h"
 #include "error.h"
 #include "lexer.h"
 #include "str.h"
 #include "error.h"
 #include "lexer.h"
 #include "str.h"