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"
-"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"
@@ -16,91 +16,91 @@ msgstr ""
 "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 ""
 
-#: src/aggregate.c:208
+#: src/aggregate.c:205
 msgid "while expecting COLUMNWISE"
 msgstr ""
 
-#: src/aggregate.c:246
+#: src/aggregate.c:243
 msgid "BREAK subcommand not specified."
 msgstr ""
 
-#: src/aggregate.c:416
+#: src/aggregate.c:397
 msgid "expecting aggregation function"
 msgstr ""
 
-#: src/aggregate.c:432
+#: src/aggregate.c:413
 #, c-format
 msgid "Unknown aggregation function %s."
 msgstr ""
 
-#: src/aggregate.c:447
+#: src/aggregate.c:428
 msgid "expecting `('"
 msgstr ""
 
-#: src/aggregate.c:482
+#: src/aggregate.c:463
 #, 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 ""
 
-#: src/aggregate.c:500 src/expr-prs.c:626
+#: src/aggregate.c:481 src/expr-prs.c:627
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/apply-dict.c:160
+#: src/apply-dict.c:163
 msgid "No matching variables found between the source and target files."
 msgstr ""
 
@@ -219,65 +219,65 @@ 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 ""
 
-#: 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 ""
 
-#: src/autorecode.c:148
+#: src/autorecode.c:149
 #, 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 ""
 
-#: src/casefile.c:184
+#: src/casefile.c:182
 #, 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 ""
 
-#: src/casefile.c:356
+#: src/casefile.c:355
 #, 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 ""
 
-#: src/casefile.c:526
+#: src/casefile.c:524
 #, 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 ""
 
-#: src/casefile.c:544
+#: src/casefile.c:543
 #, 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."
@@ -350,25 +350,25 @@ msgid ""
 "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 ""
 
-#: src/command.c:102
+#: src/command.c:103
 #, 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 ""
 
-#: src/command.c:150
+#: src/command.c:151
 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 "
@@ -376,83 +376,83 @@ msgid ""
 "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 ""
 
-#: 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 ""
 
-#: src/command.c:464
+#: src/command.c:465
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/command.c:637
+#: src/command.c:638
 #, c-format
 msgid "Error removing `%s': %s."
 msgstr ""
 
-#: src/command.c:687
+#: src/command.c:688
 #, c-format
 msgid "Couldn't fork: %s."
 msgstr ""
 
-#: src/command.c:729
+#: src/command.c:730
 #, 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 ""
 
-#: src/command.c:800
+#: src/command.c:801
 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 ""
 
-#: 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 ""
 
-#: src/compute.c:355
+#: src/compute.c:356
 #, 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 ""
 
-#: 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 "
@@ -654,259 +654,259 @@ 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 ""
 
-#: 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 ""
 
-#: src/data-list.c:393
+#: src/data-list.c:394
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/data-list.c:570
+#: src/data-list.c:571
 #, 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 ""
 
-#: src/data-list.c:652
+#: src/data-list.c:653
 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 ""
 
-#: 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 ""
 
-#: src/data-list.c:795 src/print.c:794
+#: src/data-list.c:794 src/print.c:800
 msgid "Record"
 msgstr ""
 
-#: src/data-list.c:796 src/print.c:795
+#: src/data-list.c:795 src/print.c:801
 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 ""
 
-#: 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] ""
 
-#: 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] ""
 
-#: src/data-list.c:948
+#: src/data-list.c:936
 #, 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 ""
 
-#: src/data-list.c:1002
+#: src/data-list.c:990
 #, 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 ""
 
-#: src/data-list.c:1165
+#: src/data-list.c:1153
 #, 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 ""
 
-#: src/data-list.c:1287
+#: src/data-list.c:1275
 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 ""
 
-#: src/data-list.c:1461
+#: src/data-list.c:1445
 #, 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 ""
 
-#: src/data-list.c:1540
+#: src/data-list.c:1524
 #, 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 ""
 
-#: 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 ""
 
-#: src/data-list.c:1601
+#: src/data-list.c:1585
 msgid "Missing required specification STARTS."
 msgstr ""
 
-#: src/data-list.c:1603
+#: src/data-list.c:1587
 msgid "Missing required specification OCCURS."
 msgstr ""
 
-#: src/data-list.c:1610
+#: src/data-list.c:1594
 msgid "ID specified without CONTINUED."
 msgstr ""
 
-#: src/data-list.c:1702
+#: src/data-list.c:1686
 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 ""
 
-#: src/data-list.c:1718
+#: src/data-list.c:1702
 #, 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 ""
 
-#: 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 ""
 
-#: src/data-list.c:1956
+#: src/data-list.c:1940
 #, 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 ""
 
-#: 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 ""
 
-#: src/data-list.c:1978
+#: src/data-list.c:1962
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
@@ -957,99 +957,99 @@ 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 ""
 
-#: src/descript.c:99
+#: src/descript.c:100
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/descript.c:103
+#: src/descript.c:104
 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 ""
 
-#: src/descript.c:105
+#: src/descript.c:106
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/descript.c:109 src/frequencies.q:113
+#: src/descript.c:110 src/frequencies.q:123
 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 ""
 
-#: src/descript.c:350 src/list.q:140
+#: src/descript.c:351 src/list.q:142
 msgid "No variables specified."
 msgstr ""
 
-#: src/descript.c:434
+#: src/descript.c:435
 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 ""
 
-#: src/descript.c:538
+#: src/descript.c:539
 msgid "Mapping of variables to corresponding Z-scores."
 msgstr ""
 
-#: src/descript.c:543
+#: src/descript.c:544
 msgid "Source"
 msgstr ""
 
-#: src/descript.c:544
+#: src/descript.c:545
 msgid "Target"
 msgstr ""
 
-#: src/descript.c:663 src/descript.c:669
+#: src/descript.c:664 src/descript.c:670
 msgid "Z-score of "
 msgstr ""
 
-#: src/descript.c:882
+#: src/descript.c:883
 msgid "Valid N"
 msgstr ""
 
-#: src/descript.c:883
+#: src/descript.c:884
 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 ""
@@ -1074,99 +1074,58 @@ 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 ""
 
-#: src/dfm.c:186 src/dfm.c:204
+#: src/dfm-read.c:186 src/dfm-read.c:204
 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 ""
 
-#: 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 ""
 
-#: src/dfm.c:264
+#: src/dfm-read.c:269
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/dfm.c:547
+#: src/dfm-write.c:103
 #, 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."
@@ -1235,7 +1194,7 @@ 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 ""
 
@@ -1251,54 +1210,54 @@ 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 ""
 
-#: 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."
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 "
@@ -1311,29 +1270,29 @@ msgid ""
 "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 ""
 
-#: 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 ""
 
-#: src/expr-prs.c:207
+#: src/expr-prs.c:208
 #, 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 ""
 
-#: 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 "
@@ -1341,336 +1300,336 @@ msgid ""
 "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 ""
 
-#: src/expr-prs.c:552
+#: src/expr-prs.c:553
 #, c-format
 msgid "Unknown system variable %s."
 msgstr ""
 
-#: src/expr-prs.c:591
+#: src/expr-prs.c:592
 msgid "expecting variable name"
 msgstr ""
 
-#: src/expr-prs.c:634
+#: src/expr-prs.c:635
 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 ""
 
-#: 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 ""
 
-#: src/expr-prs.c:837
+#: src/expr-prs.c:838
 #, c-format
 msgid "%s cannot take Boolean operands."
 msgstr ""
 
-#: src/expr-prs.c:869
+#: src/expr-prs.c:870
 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 ""
 
-#: src/expr-prs.c:893
+#: src/expr-prs.c:894
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/expr-prs.c:1087
+#: src/expr-prs.c:1088
 #, 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 ""
 
-#: 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 ""
 
-#: src/expr-prs.c:1168
+#: src/expr-prs.c:1169
 #, 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 ""
 
-#: src/expr-prs.c:1213
+#: src/expr-prs.c:1214
 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 ""
 
-#: 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 ""
 
-#: src/expr-prs.c:1260
+#: src/expr-prs.c:1261
 #, c-format
 msgid "expecting `)' after %s function"
 msgstr ""
 
-#: src/expr-prs.c:1282
+#: src/expr-prs.c:1283
 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 ""
 
-#: src/file-type.c:150
+#: src/file-type.c:152
 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 ""
 
-#: src/file-type.c:176
+#: src/file-type.c:178
 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 ""
 
-#: src/file-type.c:199
+#: src/file-type.c:201
 #, c-format
 msgid "WARN%s expected after DUPLICATE."
 msgstr ""
 
-#: src/file-type.c:200
+#: src/file-type.c:202
 msgid ", NOWARN, or CASE"
 msgstr ""
 
-#: src/file-type.c:201
+#: src/file-type.c:203
 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 ""
 
-#: src/file-type.c:221
+#: src/file-type.c:223
 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 ""
 
-#: src/file-type.c:240
+#: src/file-type.c:242
 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 ""
 
-#: src/file-type.c:253
+#: src/file-type.c:255
 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 ""
 
-#: src/file-type.c:267
+#: src/file-type.c:269
 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 ""
 
-#: src/file-type.c:340
+#: src/file-type.c:343
 msgid "Ending column precedes beginning column."
 msgstr ""
 
-#: src/file-type.c:360
+#: src/file-type.c:363
 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 ""
 
-#: src/file-type.c:412
+#: src/file-type.c:415
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/file-type.c:507
+#: src/file-type.c:510
 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 ""
 
-#: src/file-type.c:534
+#: src/file-type.c:537
 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 ""
 
-#: src/file-type.c:597
+#: src/file-type.c:600
 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 ""
 
-#: src/file-type.c:690
+#: src/file-type.c:693
 #, 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 ""
 
-#: src/flip.c:217
+#: src/flip.c:218
 #, 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 ""
 
-#: src/flip.c:277
+#: src/flip.c:278
 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 ""
 
-#: src/flip.c:394
+#: src/flip.c:395
 #, c-format
 msgid "Error rewinding FLIP file: %s."
 msgstr ""
 
-#: src/flip.c:398
+#: src/flip.c:399
 msgid "Error creating FLIP source file."
 msgstr ""
 
-#: src/flip.c:407
+#: src/flip.c:408
 #, 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 ""
 
-#: src/flip.c:429
+#: src/flip.c:430
 #, 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 ""
 
-#: src/flip.c:492
+#: src/flip.c:493
 #, 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 ""
 
@@ -1765,15 +1724,15 @@ 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 ""
 
-#: src/get.c:341
+#: src/get.c:395
 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 "
@@ -1781,78 +1740,78 @@ msgid ""
 "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 ""
 
-#: 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 ""
 
-#: src/get.c:433
+#: src/get.c:490
 #, 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 ""
 
-#: src/get.c:631
+#: src/get.c:746
 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 ""
 
-#: 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 ""
 
-#: src/get.c:682
+#: src/get.c:793
 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 ""
 
-#: src/get.c:727
+#: src/get.c:838
 #, 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 ""
 
-#: src/get.c:765
+#: src/get.c:877
 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 ""
 
-#: 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 ""
 
-#: src/get.c:1330
+#: src/get.c:1468
 msgid "expecting COMM or TAPE"
 msgstr ""
 
@@ -2073,7 +2032,7 @@ 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 ""
@@ -2082,27 +2041,27 @@ msgstr ""
 msgid "expecting filename"
 msgstr ""
 
-#: src/inpt-pgm.c:81
+#: src/inpt-pgm.c:82
 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 ""
 
-#: 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 ""
 
-#: src/inpt-pgm.c:342
+#: src/inpt-pgm.c:338
 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 ""
@@ -2166,7 +2125,7 @@ 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 ""
 
@@ -2207,232 +2166,232 @@ msgid ""
 "spaces."
 msgstr ""
 
-#: src/loop.c:193
+#: src/loop.c:194
 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 ""
 
-#: src/loop.c:493
+#: src/loop.c:494
 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 ""
 
-#: src/loop.c:577
+#: src/loop.c:578
 #, c-format
 msgid "%s without %s."
 msgstr ""
 
-#: src/main.c:74
+#: src/main.c:76
 msgid "Error initializing output drivers."
 msgstr ""
 
-#: src/main.c:140
+#: src/main.c:146
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/main.c:171
+#: src/main.c:177
 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 ""
 
-#: src/matrix-data.c:200
+#: src/matrix-data.c:223
 msgid "VARNAME_ cannot be explicitly specified on VARIABLES."
 msgstr ""
 
-#: src/matrix-data.c:265
+#: src/matrix-data.c:284
 msgid "in FORMAT subcommand"
 msgstr ""
 
-#: src/matrix-data.c:276
+#: src/matrix-data.c:295
 msgid "SPLIT subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:283
+#: src/matrix-data.c:302
 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 ""
 
-#: src/matrix-data.c:325
+#: src/matrix-data.c:345
 #, 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 ""
 
-#: src/matrix-data.c:355
+#: src/matrix-data.c:378
 #, 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 ""
 
-#: 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 ""
 
-#: src/matrix-data.c:389
+#: src/matrix-data.c:412
 msgid "N subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:410
+#: src/matrix-data.c:433
 msgid "CONTENTS subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:430
+#: src/matrix-data.c:453
 msgid "Nested parentheses not allowed."
 msgstr ""
 
-#: src/matrix-data.c:440
+#: src/matrix-data.c:463
 msgid "Mismatched right parenthesis (`(')."
 msgstr ""
 
-#: src/matrix-data.c:445
+#: src/matrix-data.c:468
 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 ""
 
-#: src/matrix-data.c:473
+#: src/matrix-data.c:496
 #, c-format
 msgid "Content multiply specified for %s."
 msgstr ""
 
-#: src/matrix-data.c:490
+#: src/matrix-data.c:513
 msgid "Missing right parenthesis."
 msgstr ""
 
-#: src/matrix-data.c:510
+#: src/matrix-data.c:533
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/matrix-data.c:589
+#: src/matrix-data.c:610
 msgid "No continuous variables specified."
 msgstr ""
 
-#: src/matrix-data.c:815
+#: src/matrix-data.c:853
 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 ""
 
-#: src/matrix-data.c:1070
+#: src/matrix-data.c:1106
 #, 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 ""
 
-#: src/matrix-data.c:1241
+#: src/matrix-data.c:1279
 #, 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 ""
 
-#: src/matrix-data.c:1291
+#: src/matrix-data.c:1329
 #, 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 ""
 
-#: 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 ""
 
-#: src/matrix-data.c:1692
+#: src/matrix-data.c:1729
 #, 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 ""
 
-#: src/matrix-data.c:1717
+#: src/matrix-data.c:1754
 #, 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 ""
 
-#: src/matrix-data.c:1879
+#: src/matrix-data.c:1916
 #, 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 ""
@@ -2475,79 +2434,79 @@ 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 ""
 
-#: 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 ""
 
-#: src/modify-vars.c:112
+#: src/modify-vars.c:113
 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 ""
 
-#: src/modify-vars.c:145
+#: src/modify-vars.c:146
 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 ""
 
-#: src/modify-vars.c:175
+#: src/modify-vars.c:176
 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 ""
 
-#: src/modify-vars.c:196
+#: src/modify-vars.c:197
 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 ""
 
-#: src/modify-vars.c:215
+#: src/modify-vars.c:216
 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 ""
 
-#: 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 ""
 
-#: src/modify-vars.c:297
+#: src/modify-vars.c:298
 #, c-format
 msgid "Unrecognized subcommand name `%s'."
 msgstr ""
 
-#: src/modify-vars.c:299
+#: src/modify-vars.c:300
 msgid "Subcommand name expected."
 msgstr ""
 
-#: src/modify-vars.c:307
+#: src/modify-vars.c:308
 msgid "`/' or `.' expected."
 msgstr ""
 
@@ -2555,17 +2514,17 @@ 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 ""
 
-#: 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 ""
 
-#: src/numeric.c:128
+#: src/numeric.c:129
 #, 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 ""
 
-#: 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 ""
 
-#: src/pfm-read.c:144
+#: src/pfm-read.c:142
 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 ""
 
-#: src/pfm-read.c:274
+#: src/pfm-read.c:256
 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 ""
 
-#: src/pfm-read.c:417
+#: src/pfm-read.c:390
 msgid "Bad integer format."
 msgstr ""
 
-#: src/pfm-read.c:447
+#: src/pfm-read.c:419
 #, 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 ""
 
-#: src/pfm-read.c:550
+#: src/pfm-read.c:518
 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 ""
 
-#: src/pfm-read.c:574
+#: src/pfm-read.c:542
 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 ""
 
-#: src/pfm-read.c:633
+#: src/pfm-read.c:601
 #, 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 ""
 
-#: 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 ""
 
-#: src/pfm-read.c:673
+#: src/pfm-read.c:640
 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 ""
 
-#: src/pfm-read.c:687
+#: src/pfm-read.c:654
 #, 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 ""
 
-#: src/pfm-read.c:716
+#: src/pfm-read.c:681
 msgid "Expected variable record."
 msgstr ""
 
-#: src/pfm-read.c:722
+#: src/pfm-read.c:687
 #, 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 ""
 
-#: src/pfm-read.c:744
+#: src/pfm-read.c:709
 #, 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 ""
 
-#: src/pfm-read.c:761
+#: src/pfm-read.c:726
 #, 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 ""
 
-#: src/pfm-read.c:782
+#: src/pfm-read.c:748
 #, 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 ""
 
-#: src/pfm-read.c:849
+#: src/pfm-read.c:815
 #, 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 ""
 
-#: 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 ""
 
-#: src/pfm-read.c:958
+#: src/pfm-read.c:922
 #, 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 ""
 
-#: src/pfm-read.c:1032
+#: src/pfm-read.c:978
 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 ""
 
-#: 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 ""
 
-#: src/pfm-write.c:514
-msgid "writing as a portable file"
-msgstr ""
-
 #: 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 ""
 
-#: src/print.c:180
+#: src/print.c:179
 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 ""
 
-#: src/print.c:370
+#: src/print.c:376
 #, 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 ""
 
-#: 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 ""
 
-#: src/print.c:517
+#: src/print.c:523
 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 ""
 
-#: src/print.c:678
+#: src/print.c:684
 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 ""
 
-#: src/print.c:835
+#: src/print.c:841
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/recode.c:296
+#: src/recode.c:297
 #, c-format
 msgid ""
 "There is no string variable named %s.  (All string variables specified on "
@@ -3179,83 +3094,83 @@ msgid ""
 "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 ""
 
-#: src/recode.c:324
+#: src/recode.c:325
 #, 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 ""
 
-#: 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 ""
 
-#: src/recode.c:485
+#: src/recode.c:486
 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 ""
 
-#: src/recode.c:550
+#: src/recode.c:551
 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 ""
 
-#: src/recode.c:608
+#: src/recode.c:609
 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 ""
 
-#: src/rename-vars.c:59
+#: src/rename-vars.c:60
 msgid "`(' expected."
 msgstr ""
 
-#: src/rename-vars.c:67
+#: src/rename-vars.c:68
 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 ""
 
-#: src/rename-vars.c:95
+#: src/rename-vars.c:96
 #, 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 ""
 
-#: src/repeat.c:193
+#: src/repeat.c:194
 #, 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 ""
 
-#: src/repeat.c:298
+#: src/repeat.c:299
 msgid "No commands in scope."
 msgstr ""
 
-#: src/repeat.c:486
+#: src/repeat.c:487
 msgid "No matching DO REPEAT."
 msgstr ""
 
@@ -3280,129 +3195,111 @@ 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 ""
 
-#: src/sel-if.c:105
+#: src/sel-if.c:106
 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 ""
 
-#: src/sfm-read.c:147
+#: src/sfm-read.c:140
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/sfm-read.c:288
+#: src/sfm-read.c:261
 #, 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 ""
 
-#: 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 ""
 
-#: src/sfm-read.c:387
+#: src/sfm-read.c:360
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/sfm-read.c:456 src/sfm-read.c:457
+#: src/sfm-read.c:422 src/sfm-read.c:423
 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 ""
 
-#: src/sfm-read.c:458
+#: src/sfm-read.c:424
 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 ""
 
-#: src/sfm-read.c:466
+#: src/sfm-read.c:432
 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 ""
 
-#: 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 "
@@ -3410,263 +3307,236 @@ msgid ""
 "%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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/sfm-read.c:723
+#: src/sfm-read.c:681
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/sfm-read.c:742
+#: src/sfm-read.c:700
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/sfm-read.c:783
+#: src/sfm-read.c:741
 #, 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 ""
 
-#: src/sfm-read.c:825
+#: src/sfm-read.c:779
 #, 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 ""
 
-#: src/sfm-read.c:888
+#: src/sfm-read.c:852
 #, 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 ""
 
-#: src/sfm-read.c:923
+#: src/sfm-read.c:884
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/sfm-read.c:1157
+#: src/sfm-read.c:1113
 #, 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 ""
 
-#: src/sfm-read.c:1196
+#: src/sfm-read.c:1151
 #, 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 ""
 
-#: src/sfm-read.c:1341
+#: src/sfm-read.c:1360
 #, 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
-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 ""
 
-#: 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 ""
 
-#: src/sfm-write.c:754
-msgid "writing as a system file"
-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 ""
 
-#: src/sysfile-info.c:94
+#: src/sysfile-info.c:96
 msgid "File:"
 msgstr ""
 
-#: src/sysfile-info.c:96
+#: src/sysfile-info.c:98
 msgid "Label:"
 msgstr ""
 
-#: src/sysfile-info.c:100
+#: src/sysfile-info.c:102
 msgid "No label."
 msgstr ""
 
-#: src/sysfile-info.c:103
+#: src/sysfile-info.c:105
 msgid "Created:"
 msgstr ""
 
-#: src/sysfile-info.c:106
+#: src/sysfile-info.c:108
 msgid "Endian:"
 msgstr ""
 
-#: src/sysfile-info.c:107
+#: src/sysfile-info.c:109
 msgid "Big."
 msgstr ""
 
-#: src/sysfile-info.c:107
+#: src/sysfile-info.c:109
 msgid "Little."
 msgstr ""
 
-#: src/sysfile-info.c:108
+#: src/sysfile-info.c:110
 msgid "Variables:"
 msgstr ""
 
-#: src/sysfile-info.c:111
+#: src/sysfile-info.c:113
 msgid "Cases:"
 msgstr ""
 
-#: src/sysfile-info.c:114
+#: src/sysfile-info.c:116
 msgid "Type:"
 msgstr ""
 
-#: src/sysfile-info.c:115
+#: src/sysfile-info.c:117
 msgid "System File."
 msgstr ""
 
-#: src/sysfile-info.c:116
+#: src/sysfile-info.c:118
 msgid "Weight:"
 msgstr ""
 
-#: src/sysfile-info.c:120
+#: src/sysfile-info.c:122
 msgid "Not weighted."
 msgstr ""
 
-#: src/sysfile-info.c:122
+#: src/sysfile-info.c:124
 msgid "Mode:"
 msgstr ""
 
-#: src/sysfile-info.c:124
+#: src/sysfile-info.c:126
 #, c-format
 msgid "Compression %s."
 msgstr ""
 
-#: src/sysfile-info.c:124
+#: src/sysfile-info.c:126
 msgid "on"
 msgstr ""
 
-#: src/sysfile-info.c:124
+#: src/sysfile-info.c:126
 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 ""
 
-#: src/sysfile-info.c:134 src/sysfile-info.c:368
+#: src/sysfile-info.c:136 src/sysfile-info.c:370
 msgid "Position"
 msgstr ""
 
-#: src/sysfile-info.c:191
+#: src/sysfile-info.c:193
 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 ""
 
-#: src/sysfile-info.c:256
+#: src/sysfile-info.c:258
 msgid "No variables to display."
 msgstr ""
 
-#: src/sysfile-info.c:275
+#: src/sysfile-info.c:277
 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 ""
 
-#: src/sysfile-info.c:294
+#: src/sysfile-info.c:296
 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 ""
 
-#: src/sysfile-info.c:444
+#: src/sysfile-info.c:446
 #, c-format
 msgid "Format: %s"
 msgstr ""
 
-#: src/sysfile-info.c:451
+#: src/sysfile-info.c:453
 #, c-format
 msgid "Print Format: %s"
 msgstr ""
 
-#: src/sysfile-info.c:454
+#: src/sysfile-info.c:456
 #, c-format
 msgid "Write Format: %s"
 msgstr ""
 
-#: src/sysfile-info.c:462
+#: src/sysfile-info.c:464
 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 ""
 
-#: src/sysfile-info.c:586
+#: src/sysfile-info.c:588
 msgid "No vectors defined."
 msgstr ""
 
-#: src/sysfile-info.c:601
+#: src/sysfile-info.c:603
 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 ""
 
-#: src/temporary.c:45
+#: src/temporary.c:46
 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 ""
 
-#: src/title.c:55
+#: src/title.c:56
 #, c-format
 msgid "%s before: %s\n"
 msgstr ""
 
-#: src/title.c:55
+#: src/title.c:56
 msgid "<none>"
 msgstr ""
 
-#: src/title.c:67
+#: src/title.c:68
 #, c-format
 msgid "%s: `.' expected after string."
 msgstr ""
 
-#: src/title.c:83
+#: src/title.c:84
 #, 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 ""
@@ -3889,29 +3759,29 @@ 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 ""
 
-#: src/vars-prs.c:100
+#: src/vars-prs.c:101
 msgid "ordinary"
 msgstr ""
 
-#: src/vars-prs.c:102
+#: src/vars-prs.c:103
 msgid "system"
 msgstr ""
 
-#: src/vars-prs.c:104
+#: src/vars-prs.c:105
 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 ""
 
-#: 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 "
@@ -3919,700 +3789,706 @@ msgid ""
 "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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/vars-prs.c:272
+#: src/vars-prs.c:273
 #, 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 ""
 
-#: src/vars-prs.c:399
+#: src/vars-prs.c:400
 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 ""
 
-#: src/vars-prs.c:426
+#: src/vars-prs.c:427
 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 ""
 
-#: src/vector.c:72
+#: src/vector.c:73
 #, 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 ""
 
-#: src/vector.c:127
+#: src/vector.c:128
 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 ""
 
-#: 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 ""
 
-#: src/weight.c:52
+#: src/weight.c:53
 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 ""
 
-#: src/crosstabs.q:241
+#: src/crosstabs.q:261
 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 ""
 
-#: src/crosstabs.q:312
+#: src/crosstabs.q:332
 msgid "expecting BY"
 msgstr ""
 
-#: src/crosstabs.q:379
+#: src/crosstabs.q:399
 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 ""
 
-#: src/crosstabs.q:769
+#: src/crosstabs.q:800
 msgid "Summary."
 msgstr ""
 
-#: src/crosstabs.q:771 src/examine.q:784
+#: src/crosstabs.q:802 src/examine.q:837
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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/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 ""
 
-#: src/crosstabs.q:1027
+#: src/crosstabs.q:1058
 msgid "count"
 msgstr ""
 
-#: src/crosstabs.q:1028
+#: src/crosstabs.q:1059
 msgid "row %"
 msgstr ""
 
-#: src/crosstabs.q:1029
+#: src/crosstabs.q:1060
 msgid "column %"
 msgstr ""
 
-#: src/crosstabs.q:1030
+#: src/crosstabs.q:1061
 msgid "total %"
 msgstr ""
 
-#: src/crosstabs.q:1031
+#: src/crosstabs.q:1062
 msgid "expected"
 msgstr ""
 
-#: src/crosstabs.q:1032
+#: src/crosstabs.q:1063
 msgid "residual"
 msgstr ""
 
-#: src/crosstabs.q:1033
+#: src/crosstabs.q:1064
 msgid "std. resid."
 msgstr ""
 
-#: src/crosstabs.q:1034
+#: src/crosstabs.q:1065
 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 ""
 
-#: 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 ""
 
-#: src/crosstabs.q:1071
+#: src/crosstabs.q:1102
 msgid "Asymp. Sig. (2-sided)"
 msgstr ""
 
-#: src/crosstabs.q:1073
+#: src/crosstabs.q:1104
 msgid "Exact. Sig. (2-sided)"
 msgstr ""
 
-#: src/crosstabs.q:1075
+#: src/crosstabs.q:1106
 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 ""
 
-#: src/crosstabs.q:1096 src/crosstabs.q:1138
+#: src/crosstabs.q:1127 src/crosstabs.q:1169
 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 ""
 
-#: src/crosstabs.q:1098 src/crosstabs.q:1140
+#: src/crosstabs.q:1129 src/crosstabs.q:1171
 msgid "Approx. Sig."
 msgstr ""
 
-#: src/crosstabs.q:1113
+#: src/crosstabs.q:1144
 #, 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 ""
 
-#: 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 ""
 
-#: src/crosstabs.q:1136
+#: src/crosstabs.q:1167
 msgid "Type"
 msgstr ""
 
-#: src/crosstabs.q:1884
+#: src/crosstabs.q:1916
 msgid "Pearson Chi-Square"
 msgstr ""
 
-#: src/crosstabs.q:1885
+#: src/crosstabs.q:1917
 msgid "Likelihood Ratio"
 msgstr ""
 
-#: src/crosstabs.q:1886
+#: src/crosstabs.q:1918
 msgid "Fisher's Exact Test"
 msgstr ""
 
-#: src/crosstabs.q:1887
+#: src/crosstabs.q:1919
 msgid "Continuity Correction"
 msgstr ""
 
-#: src/crosstabs.q:1888
+#: src/crosstabs.q:1920
 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 ""
 
-#: src/crosstabs.q:1941 src/crosstabs.q:2070
+#: src/crosstabs.q:1973 src/crosstabs.q:2102
 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 ""
 
-#: src/crosstabs.q:1943
+#: src/crosstabs.q:1975
 msgid "Interval by Interval"
 msgstr ""
 
-#: src/crosstabs.q:1944
+#: src/crosstabs.q:1976
 msgid "Measure of Agreement"
 msgstr ""
 
-#: src/crosstabs.q:1949
+#: src/crosstabs.q:1981
 msgid "Phi"
 msgstr ""
 
-#: src/crosstabs.q:1950
+#: src/crosstabs.q:1982
 msgid "Cramer's V"
 msgstr ""
 
-#: src/crosstabs.q:1951
+#: src/crosstabs.q:1983
 msgid "Contingency Coefficient"
 msgstr ""
 
-#: src/crosstabs.q:1952
+#: src/crosstabs.q:1984
 msgid "Kendall's tau-b"
 msgstr ""
 
-#: src/crosstabs.q:1953
+#: src/crosstabs.q:1985
 msgid "Kendall's tau-c"
 msgstr ""
 
-#: src/crosstabs.q:1954
+#: src/crosstabs.q:1986
 msgid "Gamma"
 msgstr ""
 
-#: src/crosstabs.q:1955
+#: src/crosstabs.q:1987
 msgid "Spearman Correlation"
 msgstr ""
 
-#: src/crosstabs.q:1956
+#: src/crosstabs.q:1988
 msgid "Pearson's R"
 msgstr ""
 
-#: src/crosstabs.q:1957
+#: src/crosstabs.q:1989
 msgid "Kappa"
 msgstr ""
 
-#: src/crosstabs.q:2027
+#: src/crosstabs.q:2059
 #, 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 ""
 
-#: src/crosstabs.q:2038
+#: src/crosstabs.q:2070
 #, c-format
 msgid "For cohort %s = %g"
 msgstr ""
 
-#: src/crosstabs.q:2041
+#: src/crosstabs.q:2073
 #, c-format
 msgid "For cohort %s = %.*s"
 msgstr ""
 
-#: src/crosstabs.q:2072
+#: src/crosstabs.q:2104
 msgid "Nominal by Interval"
 msgstr ""
 
-#: src/crosstabs.q:2077
+#: src/crosstabs.q:2109
 msgid "Lambda"
 msgstr ""
 
-#: src/crosstabs.q:2078
+#: src/crosstabs.q:2110
 msgid "Goodman and Kruskal tau"
 msgstr ""
 
-#: src/crosstabs.q:2079
+#: src/crosstabs.q:2111
 msgid "Uncertainty Coefficient"
 msgstr ""
 
-#: src/crosstabs.q:2080
+#: src/crosstabs.q:2112
 msgid "Somers' d"
 msgstr ""
 
-#: src/crosstabs.q:2081
+#: src/crosstabs.q:2113
 msgid "Eta"
 msgstr ""
 
-#: src/crosstabs.q:2086
+#: src/crosstabs.q:2118
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/examine.q:563 src/oneway.q:410
+#: src/examine.q:610 src/oneway.q:412
 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 ""
 
-#: src/examine.q:608 src/oneway.q:403
+#: src/examine.q:655 src/oneway.q:405
 msgid "Lower Bound"
 msgstr ""
 
-#: src/examine.q:619 src/oneway.q:404
+#: src/examine.q:666 src/oneway.q:406
 msgid "Upper Bound"
 msgstr ""
 
-#: src/examine.q:631
+#: src/examine.q:678
 msgid "5% Trimmed Mean"
 msgstr ""
 
-#: src/examine.q:636 src/frequencies.q:102
+#: src/examine.q:689 src/frequencies.q:112
 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 ""
 
-#: src/examine.q:701
+#: src/examine.q:754
 msgid "Interquartile Range"
 msgstr ""
 
-#: src/examine.q:778
+#: src/examine.q:831
 msgid "Case Processing Summary"
 msgstr ""
 
-#: src/examine.q:1103
+#: src/examine.q:1157
 msgid "Extreme Values"
 msgstr ""
 
-#: src/examine.q:1126
+#: src/examine.q:1180
 msgid "Case Number"
 msgstr ""
 
-#: src/examine.q:1252
+#: src/examine.q:1306
 msgid "Highest"
 msgstr ""
 
-#: src/examine.q:1257
+#: src/examine.q:1311
 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 ""
 
-#: src/file-handle.q:147
+#: src/file-handle.q:144
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/file-handle.q:262
+#: src/file-handle.q:317
 msgid "expecting a file name or handle name"
 msgstr ""
 
-#: src/frequencies.q:101
+#: src/frequencies.q:111
 msgid "S.E. Mean"
 msgstr ""
 
-#: src/frequencies.q:103
+#: src/frequencies.q:113
 msgid "Mode"
 msgstr ""
 
-#: src/frequencies.q:107
+#: src/frequencies.q:117
 msgid "S.E. Kurt"
 msgstr ""
 
-#: src/frequencies.q:109
+#: src/frequencies.q:119
 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 ""
 
-#: 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 ""
 
-#: src/frequencies.q:722
+#: src/frequencies.q:747
 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 ""
 
-#: src/frequencies.q:747
+#: src/frequencies.q:766
 #, 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 ""
 
-#: src/frequencies.q:822
+#: src/frequencies.q:844
 #, 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 ""
 
-#: 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 ""
 
-#: src/frequencies.q:1057
+#: src/frequencies.q:1084
 msgid "Frequency"
 msgstr ""
 
-#: src/frequencies.q:1076
+#: src/frequencies.q:1104
 msgid "Value Label"
 msgstr ""
 
-#: src/frequencies.q:1175
+#: src/frequencies.q:1205
 msgid "Freq"
 msgstr ""
 
-#: src/frequencies.q:1176 src/frequencies.q:1178
+#: src/frequencies.q:1206 src/frequencies.q:1208
 msgid "Pct"
 msgstr ""
 
-#: src/frequencies.q:1394
+#: src/frequencies.q:1427
 #, c-format
 msgid "No valid data for variable %s; statistics not displayed."
 msgstr ""
 
-#: src/frequencies.q:1433
+#: src/frequencies.q:1465
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/list.q:195
+#: src/list.q:197
 msgid "`/FORMAT WEIGHT' specified, but weighting is not on."
 msgstr ""
 
-#: src/list.q:436
+#: src/list.q:438
 msgid "Line"
 msgstr ""
 
-#: src/means.q:101
+#: src/means.q:100
 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 ""
 
-#: src/oneway.q:166
+#: src/oneway.q:168
 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 ""
 
-#: 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 ""
 
-#: src/oneway.q:275
+#: src/oneway.q:277
 msgid "Sum of Squares"
 msgstr ""
 
-#: src/oneway.q:277
+#: src/oneway.q:279
 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 ""
 
-#: src/oneway.q:279 src/oneway.q:549
+#: src/oneway.q:281 src/oneway.q:552
 msgid "Significance"
 msgstr ""
 
-#: src/oneway.q:304
+#: src/oneway.q:305
 msgid "Between Groups"
 msgstr ""
 
-#: src/oneway.q:305
+#: src/oneway.q:306
 msgid "Within Groups"
 msgstr ""
 
-#: src/oneway.q:351
+#: src/oneway.q:353
 msgid "ANOVA"
 msgstr ""
 
-#: src/oneway.q:546
+#: src/oneway.q:549
 msgid "Levene Statistic"
 msgstr ""
 
-#: src/oneway.q:547
+#: src/oneway.q:550
 msgid "df1"
 msgstr ""
 
-#: src/oneway.q:548
+#: src/oneway.q:551
 msgid "df2"
 msgstr ""
 
-#: src/oneway.q:552
+#: src/oneway.q:555
 msgid "Test of Homogeneity of Variances"
 msgstr ""
 
-#: src/oneway.q:628
+#: src/oneway.q:631
 msgid "Contrast Coefficients"
 msgstr ""
 
-#: src/oneway.q:630 src/oneway.q:707
+#: src/oneway.q:633 src/oneway.q:703
 msgid "Contrast"
 msgstr ""
 
-#: src/oneway.q:705
+#: src/oneway.q:701
 msgid "Contrast Tests"
 msgstr ""
 
-#: src/oneway.q:708
+#: src/oneway.q:704
 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 ""
 
-#: 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 ""
 
-#: src/oneway.q:760
+#: src/oneway.q:754
 msgid "Assume equal variances"
 msgstr ""
 
-#: src/oneway.q:764
+#: src/oneway.q:758
 msgid "Does not assume equal"
 msgstr ""
 
@@ -4748,115 +4624,115 @@ msgstr ""
 msgid "data> "
 msgstr ""
 
-#: src/t-test.q:266
+#: src/t-test.q:268
 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 ""
 
-#: src/t-test.q:320
+#: src/t-test.q:322
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/t-test.q:501
+#: src/t-test.q:503
 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 ""
 
-#: 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 ""
 
-#: src/t-test.q:700
+#: src/t-test.q:702
 msgid "Group Statistics"
 msgstr ""
 
-#: src/t-test.q:824
+#: src/t-test.q:826
 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 ""
 
-#: src/t-test.q:964
+#: src/t-test.q:965
 msgid "Independent Samples Test"
 msgstr ""
 
-#: src/t-test.q:972
+#: src/t-test.q:973
 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 ""
 
-#: src/t-test.q:977 src/t-test.q:1367
+#: src/t-test.q:978 src/t-test.q:1367
 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 ""
 
-#: src/t-test.q:982
+#: src/t-test.q:983
 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 ""
 
-#: src/t-test.q:1041
+#: src/t-test.q:1043
 msgid "Equal variances assumed"
 msgstr ""
 
-#: src/t-test.q:1094
+#: src/t-test.q:1095
 msgid "Equal variances not assumed"
 msgstr ""
 
-#: src/t-test.q:1152
+#: src/t-test.q:1153
 msgid "Paired Samples Test"
 msgstr ""
 
-#: src/t-test.q:1155
+#: src/t-test.q:1156
 msgid "Paired Differences"
 msgstr ""
 
-#: src/t-test.q:1167
+#: src/t-test.q:1168
 msgid "Std. Error Mean"
 msgstr ""
 
-#: src/t-test.q:1246
+#: src/t-test.q:1247
 msgid "One-Sample Test"
 msgstr ""
 
-#: src/t-test.q:1251
+#: src/t-test.q:1252
 #, 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"
-"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"
@@ -17,91 +17,91 @@ msgstr ""
 "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 ""
 
-#: src/aggregate.c:208
+#: src/aggregate.c:205
 msgid "while expecting COLUMNWISE"
 msgstr ""
 
-#: src/aggregate.c:246
+#: src/aggregate.c:243
 msgid "BREAK subcommand not specified."
 msgstr ""
 
-#: src/aggregate.c:416
+#: src/aggregate.c:397
 msgid "expecting aggregation function"
 msgstr ""
 
-#: src/aggregate.c:432
+#: src/aggregate.c:413
 #, c-format
 msgid "Unknown aggregation function %s."
 msgstr ""
 
-#: src/aggregate.c:447
+#: src/aggregate.c:428
 msgid "expecting `('"
 msgstr ""
 
-#: src/aggregate.c:482
+#: src/aggregate.c:463
 #, 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 ""
 
-#: src/aggregate.c:500 src/expr-prs.c:626
+#: src/aggregate.c:481 src/expr-prs.c:627
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/apply-dict.c:160
+#: src/apply-dict.c:163
 msgid "No matching variables found between the source and target files."
 msgstr ""
 
@@ -220,65 +220,65 @@ 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 ""
 
-#: 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 ""
 
-#: src/autorecode.c:148
+#: src/autorecode.c:149
 #, 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 ""
 
-#: src/casefile.c:184
+#: src/casefile.c:182
 #, 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 ""
 
-#: src/casefile.c:356
+#: src/casefile.c:355
 #, 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 ""
 
-#: src/casefile.c:526
+#: src/casefile.c:524
 #, 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 ""
 
-#: src/casefile.c:544
+#: src/casefile.c:543
 #, 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."
@@ -351,25 +351,25 @@ msgid ""
 "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 ""
 
-#: src/command.c:102
+#: src/command.c:103
 #, 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 ""
 
-#: src/command.c:150
+#: src/command.c:151
 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 "
@@ -377,83 +377,83 @@ msgid ""
 "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 ""
 
-#: 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 ""
 
-#: src/command.c:464
+#: src/command.c:465
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/command.c:637
+#: src/command.c:638
 #, c-format
 msgid "Error removing `%s': %s."
 msgstr ""
 
-#: src/command.c:687
+#: src/command.c:688
 #, c-format
 msgid "Couldn't fork: %s."
 msgstr ""
 
-#: src/command.c:729
+#: src/command.c:730
 #, 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 ""
 
-#: src/command.c:800
+#: src/command.c:801
 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 ""
 
-#: 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 ""
 
-#: src/compute.c:355
+#: src/compute.c:356
 #, 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 ""
 
-#: 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 "
@@ -655,259 +655,259 @@ 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 ""
 
-#: 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 ""
 
-#: src/data-list.c:393
+#: src/data-list.c:394
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/data-list.c:570
+#: src/data-list.c:571
 #, 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 ""
 
-#: src/data-list.c:652
+#: src/data-list.c:653
 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 ""
 
-#: 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 ""
 
-#: src/data-list.c:795 src/print.c:794
+#: src/data-list.c:794 src/print.c:800
 msgid "Record"
 msgstr ""
 
-#: src/data-list.c:796 src/print.c:795
+#: src/data-list.c:795 src/print.c:801
 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 ""
 
-#: 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] ""
 
-#: 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] ""
 
-#: src/data-list.c:948
+#: src/data-list.c:936
 #, 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 ""
 
-#: src/data-list.c:1002
+#: src/data-list.c:990
 #, 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 ""
 
-#: src/data-list.c:1165
+#: src/data-list.c:1153
 #, 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 ""
 
-#: src/data-list.c:1287
+#: src/data-list.c:1275
 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 ""
 
-#: src/data-list.c:1461
+#: src/data-list.c:1445
 #, 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 ""
 
-#: src/data-list.c:1540
+#: src/data-list.c:1524
 #, 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 ""
 
-#: 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 ""
 
-#: src/data-list.c:1601
+#: src/data-list.c:1585
 msgid "Missing required specification STARTS."
 msgstr ""
 
-#: src/data-list.c:1603
+#: src/data-list.c:1587
 msgid "Missing required specification OCCURS."
 msgstr ""
 
-#: src/data-list.c:1610
+#: src/data-list.c:1594
 msgid "ID specified without CONTINUED."
 msgstr ""
 
-#: src/data-list.c:1702
+#: src/data-list.c:1686
 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 ""
 
-#: src/data-list.c:1718
+#: src/data-list.c:1702
 #, 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 ""
 
-#: 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 ""
 
-#: src/data-list.c:1956
+#: src/data-list.c:1940
 #, 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 ""
 
-#: 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 ""
 
-#: src/data-list.c:1978
+#: src/data-list.c:1962
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
@@ -958,99 +958,99 @@ 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 ""
 
-#: src/descript.c:99
+#: src/descript.c:100
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/descript.c:103
+#: src/descript.c:104
 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 ""
 
-#: src/descript.c:105
+#: src/descript.c:106
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/descript.c:109 src/frequencies.q:113
+#: src/descript.c:110 src/frequencies.q:123
 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 ""
 
-#: src/descript.c:350 src/list.q:140
+#: src/descript.c:351 src/list.q:142
 msgid "No variables specified."
 msgstr ""
 
-#: src/descript.c:434
+#: src/descript.c:435
 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 ""
 
-#: src/descript.c:538
+#: src/descript.c:539
 msgid "Mapping of variables to corresponding Z-scores."
 msgstr ""
 
-#: src/descript.c:543
+#: src/descript.c:544
 msgid "Source"
 msgstr ""
 
-#: src/descript.c:544
+#: src/descript.c:545
 msgid "Target"
 msgstr ""
 
-#: src/descript.c:663 src/descript.c:669
+#: src/descript.c:664 src/descript.c:670
 msgid "Z-score of "
 msgstr ""
 
-#: src/descript.c:882
+#: src/descript.c:883
 msgid "Valid N"
 msgstr ""
 
-#: src/descript.c:883
+#: src/descript.c:884
 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 ""
@@ -1075,99 +1075,58 @@ 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 ""
 
-#: src/dfm.c:186 src/dfm.c:204
+#: src/dfm-read.c:186 src/dfm-read.c:204
 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 ""
 
-#: 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 ""
 
-#: src/dfm.c:264
+#: src/dfm-read.c:269
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/dfm.c:547
+#: src/dfm-write.c:103
 #, 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."
@@ -1236,7 +1195,7 @@ 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 ""
 
@@ -1252,53 +1211,53 @@ 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 ""
 
-#: src/expr-evl.c:697
+#: src/expr-evl.c:698
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 "
@@ -1311,29 +1270,29 @@ msgid ""
 "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 ""
 
-#: 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 ""
 
-#: src/expr-prs.c:207
+#: src/expr-prs.c:208
 #, 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 ""
 
-#: 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 "
@@ -1341,336 +1300,336 @@ msgid ""
 "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 ""
 
-#: src/expr-prs.c:552
+#: src/expr-prs.c:553
 #, c-format
 msgid "Unknown system variable %s."
 msgstr ""
 
-#: src/expr-prs.c:591
+#: src/expr-prs.c:592
 msgid "expecting variable name"
 msgstr ""
 
-#: src/expr-prs.c:634
+#: src/expr-prs.c:635
 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 ""
 
-#: 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 ""
 
-#: src/expr-prs.c:837
+#: src/expr-prs.c:838
 #, c-format
 msgid "%s cannot take Boolean operands."
 msgstr ""
 
-#: src/expr-prs.c:869
+#: src/expr-prs.c:870
 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 ""
 
-#: src/expr-prs.c:893
+#: src/expr-prs.c:894
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/expr-prs.c:1087
+#: src/expr-prs.c:1088
 #, 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 ""
 
-#: 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 ""
 
-#: src/expr-prs.c:1168
+#: src/expr-prs.c:1169
 #, 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 ""
 
-#: src/expr-prs.c:1213
+#: src/expr-prs.c:1214
 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 ""
 
-#: 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 ""
 
-#: src/expr-prs.c:1260
+#: src/expr-prs.c:1261
 #, c-format
 msgid "expecting `)' after %s function"
 msgstr ""
 
-#: src/expr-prs.c:1282
+#: src/expr-prs.c:1283
 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 ""
 
-#: src/file-type.c:150
+#: src/file-type.c:152
 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 ""
 
-#: src/file-type.c:176
+#: src/file-type.c:178
 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 ""
 
-#: src/file-type.c:199
+#: src/file-type.c:201
 #, c-format
 msgid "WARN%s expected after DUPLICATE."
 msgstr ""
 
-#: src/file-type.c:200
+#: src/file-type.c:202
 msgid ", NOWARN, or CASE"
 msgstr ""
 
-#: src/file-type.c:201
+#: src/file-type.c:203
 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 ""
 
-#: src/file-type.c:221
+#: src/file-type.c:223
 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 ""
 
-#: src/file-type.c:240
+#: src/file-type.c:242
 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 ""
 
-#: src/file-type.c:253
+#: src/file-type.c:255
 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 ""
 
-#: src/file-type.c:267
+#: src/file-type.c:269
 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 ""
 
-#: src/file-type.c:340
+#: src/file-type.c:343
 msgid "Ending column precedes beginning column."
 msgstr ""
 
-#: src/file-type.c:360
+#: src/file-type.c:363
 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 ""
 
-#: src/file-type.c:412
+#: src/file-type.c:415
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/file-type.c:507
+#: src/file-type.c:510
 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 ""
 
-#: src/file-type.c:534
+#: src/file-type.c:537
 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 ""
 
-#: src/file-type.c:597
+#: src/file-type.c:600
 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 ""
 
-#: src/file-type.c:690
+#: src/file-type.c:693
 #, 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 ""
 
-#: src/flip.c:217
+#: src/flip.c:218
 #, 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 ""
 
-#: src/flip.c:277
+#: src/flip.c:278
 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 ""
 
-#: src/flip.c:394
+#: src/flip.c:395
 #, c-format
 msgid "Error rewinding FLIP file: %s."
 msgstr ""
 
-#: src/flip.c:398
+#: src/flip.c:399
 msgid "Error creating FLIP source file."
 msgstr ""
 
-#: src/flip.c:407
+#: src/flip.c:408
 #, 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 ""
 
-#: src/flip.c:429
+#: src/flip.c:430
 #, 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 ""
 
-#: src/flip.c:492
+#: src/flip.c:493
 #, 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 ""
 
@@ -1765,15 +1724,15 @@ 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 ""
 
-#: src/get.c:341
+#: src/get.c:395
 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 "
@@ -1781,78 +1740,78 @@ msgid ""
 "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 ""
 
-#: 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 ""
 
-#: src/get.c:433
+#: src/get.c:490
 #, 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 ""
 
-#: src/get.c:631
+#: src/get.c:746
 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 ""
 
-#: 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 ""
 
-#: src/get.c:682
+#: src/get.c:793
 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 ""
 
-#: src/get.c:727
+#: src/get.c:838
 #, 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 ""
 
-#: src/get.c:765
+#: src/get.c:877
 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 ""
 
-#: 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 ""
 
-#: src/get.c:1330
+#: src/get.c:1468
 msgid "expecting COMM or TAPE"
 msgstr ""
 
@@ -2073,7 +2032,7 @@ 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 ""
@@ -2082,27 +2041,27 @@ msgstr ""
 msgid "expecting filename"
 msgstr ""
 
-#: src/inpt-pgm.c:81
+#: src/inpt-pgm.c:82
 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 ""
 
-#: 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 ""
 
-#: src/inpt-pgm.c:342
+#: src/inpt-pgm.c:338
 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 ""
@@ -2166,7 +2125,7 @@ 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 ""
 
@@ -2207,232 +2166,232 @@ msgid ""
 "spaces."
 msgstr ""
 
-#: src/loop.c:193
+#: src/loop.c:194
 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 ""
 
-#: src/loop.c:493
+#: src/loop.c:494
 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 ""
 
-#: src/loop.c:577
+#: src/loop.c:578
 #, c-format
 msgid "%s without %s."
 msgstr ""
 
-#: src/main.c:74
+#: src/main.c:76
 msgid "Error initializing output drivers."
 msgstr ""
 
-#: src/main.c:140
+#: src/main.c:146
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/main.c:171
+#: src/main.c:177
 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 ""
 
-#: src/matrix-data.c:200
+#: src/matrix-data.c:223
 msgid "VARNAME_ cannot be explicitly specified on VARIABLES."
 msgstr ""
 
-#: src/matrix-data.c:265
+#: src/matrix-data.c:284
 msgid "in FORMAT subcommand"
 msgstr ""
 
-#: src/matrix-data.c:276
+#: src/matrix-data.c:295
 msgid "SPLIT subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:283
+#: src/matrix-data.c:302
 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 ""
 
-#: src/matrix-data.c:325
+#: src/matrix-data.c:345
 #, 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 ""
 
-#: src/matrix-data.c:355
+#: src/matrix-data.c:378
 #, 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 ""
 
-#: 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 ""
 
-#: src/matrix-data.c:389
+#: src/matrix-data.c:412
 msgid "N subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:410
+#: src/matrix-data.c:433
 msgid "CONTENTS subcommand multiply specified."
 msgstr ""
 
-#: src/matrix-data.c:430
+#: src/matrix-data.c:453
 msgid "Nested parentheses not allowed."
 msgstr ""
 
-#: src/matrix-data.c:440
+#: src/matrix-data.c:463
 msgid "Mismatched right parenthesis (`(')."
 msgstr ""
 
-#: src/matrix-data.c:445
+#: src/matrix-data.c:468
 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 ""
 
-#: src/matrix-data.c:473
+#: src/matrix-data.c:496
 #, c-format
 msgid "Content multiply specified for %s."
 msgstr ""
 
-#: src/matrix-data.c:490
+#: src/matrix-data.c:513
 msgid "Missing right parenthesis."
 msgstr ""
 
-#: src/matrix-data.c:510
+#: src/matrix-data.c:533
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/matrix-data.c:589
+#: src/matrix-data.c:610
 msgid "No continuous variables specified."
 msgstr ""
 
-#: src/matrix-data.c:815
+#: src/matrix-data.c:853
 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 ""
 
-#: src/matrix-data.c:1070
+#: src/matrix-data.c:1106
 #, 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 ""
 
-#: src/matrix-data.c:1241
+#: src/matrix-data.c:1279
 #, 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 ""
 
-#: src/matrix-data.c:1291
+#: src/matrix-data.c:1329
 #, 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 ""
 
-#: 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 ""
 
-#: src/matrix-data.c:1692
+#: src/matrix-data.c:1729
 #, 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 ""
 
-#: src/matrix-data.c:1717
+#: src/matrix-data.c:1754
 #, 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 ""
 
-#: src/matrix-data.c:1879
+#: src/matrix-data.c:1916
 #, 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 ""
@@ -2475,79 +2434,79 @@ 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 ""
 
-#: 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 ""
 
-#: src/modify-vars.c:112
+#: src/modify-vars.c:113
 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 ""
 
-#: src/modify-vars.c:145
+#: src/modify-vars.c:146
 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 ""
 
-#: src/modify-vars.c:175
+#: src/modify-vars.c:176
 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 ""
 
-#: src/modify-vars.c:196
+#: src/modify-vars.c:197
 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 ""
 
-#: src/modify-vars.c:215
+#: src/modify-vars.c:216
 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 ""
 
-#: 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 ""
 
-#: src/modify-vars.c:297
+#: src/modify-vars.c:298
 #, c-format
 msgid "Unrecognized subcommand name `%s'."
 msgstr ""
 
-#: src/modify-vars.c:299
+#: src/modify-vars.c:300
 msgid "Subcommand name expected."
 msgstr ""
 
-#: src/modify-vars.c:307
+#: src/modify-vars.c:308
 msgid "`/' or `.' expected."
 msgstr ""
 
@@ -2555,17 +2514,17 @@ 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 ""
 
-#: 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 ""
 
-#: src/numeric.c:128
+#: src/numeric.c:129
 #, 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 ""
 
-#: 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 ""
 
-#: src/pfm-read.c:144
+#: src/pfm-read.c:142
 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 ""
 
-#: src/pfm-read.c:274
+#: src/pfm-read.c:256
 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 ""
 
-#: src/pfm-read.c:417
+#: src/pfm-read.c:390
 msgid "Bad integer format."
 msgstr ""
 
-#: src/pfm-read.c:447
+#: src/pfm-read.c:419
 #, 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 ""
 
-#: src/pfm-read.c:550
+#: src/pfm-read.c:518
 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 ""
 
-#: src/pfm-read.c:574
+#: src/pfm-read.c:542
 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 ""
 
-#: src/pfm-read.c:633
+#: src/pfm-read.c:601
 #, 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 ""
 
-#: 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 ""
 
-#: src/pfm-read.c:673
+#: src/pfm-read.c:640
 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 ""
 
-#: src/pfm-read.c:687
+#: src/pfm-read.c:654
 #, 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 ""
 
-#: src/pfm-read.c:716
+#: src/pfm-read.c:681
 msgid "Expected variable record."
 msgstr ""
 
-#: src/pfm-read.c:722
+#: src/pfm-read.c:687
 #, 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 ""
 
-#: src/pfm-read.c:744
+#: src/pfm-read.c:709
 #, 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 ""
 
-#: src/pfm-read.c:761
+#: src/pfm-read.c:726
 #, 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 ""
 
-#: src/pfm-read.c:782
+#: src/pfm-read.c:748
 #, 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 ""
 
-#: src/pfm-read.c:849
+#: src/pfm-read.c:815
 #, 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 ""
 
-#: 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 ""
 
-#: src/pfm-read.c:958
+#: src/pfm-read.c:922
 #, 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 ""
 
-#: src/pfm-read.c:1032
+#: src/pfm-read.c:978
 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 ""
 
-#: 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 ""
 
-#: src/pfm-write.c:514
-msgid "writing as a portable file"
-msgstr ""
-
 #: 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 ""
 
-#: src/print.c:180
+#: src/print.c:179
 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 ""
 
-#: src/print.c:370
+#: src/print.c:376
 #, 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 ""
 
-#: 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 ""
 
-#: src/print.c:517
+#: src/print.c:523
 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 ""
 
-#: src/print.c:678
+#: src/print.c:684
 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 ""
 
-#: src/print.c:835
+#: src/print.c:841
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/recode.c:296
+#: src/recode.c:297
 #, c-format
 msgid ""
 "There is no string variable named %s.  (All string variables specified on "
@@ -3179,83 +3094,83 @@ msgid ""
 "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 ""
 
-#: src/recode.c:324
+#: src/recode.c:325
 #, 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 ""
 
-#: 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 ""
 
-#: src/recode.c:485
+#: src/recode.c:486
 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 ""
 
-#: src/recode.c:550
+#: src/recode.c:551
 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 ""
 
-#: src/recode.c:608
+#: src/recode.c:609
 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 ""
 
-#: src/rename-vars.c:59
+#: src/rename-vars.c:60
 msgid "`(' expected."
 msgstr ""
 
-#: src/rename-vars.c:67
+#: src/rename-vars.c:68
 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 ""
 
-#: src/rename-vars.c:95
+#: src/rename-vars.c:96
 #, 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 ""
 
-#: src/repeat.c:193
+#: src/repeat.c:194
 #, 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 ""
 
-#: src/repeat.c:298
+#: src/repeat.c:299
 msgid "No commands in scope."
 msgstr ""
 
-#: src/repeat.c:486
+#: src/repeat.c:487
 msgid "No matching DO REPEAT."
 msgstr ""
 
@@ -3280,129 +3195,111 @@ 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 ""
 
-#: src/sel-if.c:105
+#: src/sel-if.c:106
 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 ""
 
-#: src/sfm-read.c:147
+#: src/sfm-read.c:140
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/sfm-read.c:288
+#: src/sfm-read.c:261
 #, 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 ""
 
-#: 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 ""
 
-#: src/sfm-read.c:387
+#: src/sfm-read.c:360
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/sfm-read.c:456 src/sfm-read.c:457
+#: src/sfm-read.c:422 src/sfm-read.c:423
 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 ""
 
-#: src/sfm-read.c:458
+#: src/sfm-read.c:424
 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 ""
 
-#: src/sfm-read.c:466
+#: src/sfm-read.c:432
 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 ""
 
-#: 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 "
@@ -3410,263 +3307,236 @@ msgid ""
 "%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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/sfm-read.c:723
+#: src/sfm-read.c:681
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/sfm-read.c:742
+#: src/sfm-read.c:700
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/sfm-read.c:783
+#: src/sfm-read.c:741
 #, 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 ""
 
-#: src/sfm-read.c:825
+#: src/sfm-read.c:779
 #, 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 ""
 
-#: src/sfm-read.c:888
+#: src/sfm-read.c:852
 #, 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 ""
 
-#: src/sfm-read.c:923
+#: src/sfm-read.c:884
 #, 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/sfm-read.c:1157
+#: src/sfm-read.c:1113
 #, 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 ""
 
-#: src/sfm-read.c:1196
+#: src/sfm-read.c:1151
 #, 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 ""
 
-#: src/sfm-read.c:1341
+#: src/sfm-read.c:1360
 #, 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
-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 ""
 
-#: 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 ""
 
-#: src/sfm-write.c:754
-msgid "writing as a system file"
-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 ""
 
-#: src/sysfile-info.c:94
+#: src/sysfile-info.c:96
 msgid "File:"
 msgstr ""
 
-#: src/sysfile-info.c:96
+#: src/sysfile-info.c:98
 msgid "Label:"
 msgstr ""
 
-#: src/sysfile-info.c:100
+#: src/sysfile-info.c:102
 msgid "No label."
 msgstr ""
 
-#: src/sysfile-info.c:103
+#: src/sysfile-info.c:105
 msgid "Created:"
 msgstr ""
 
-#: src/sysfile-info.c:106
+#: src/sysfile-info.c:108
 msgid "Endian:"
 msgstr ""
 
-#: src/sysfile-info.c:107
+#: src/sysfile-info.c:109
 msgid "Big."
 msgstr ""
 
-#: src/sysfile-info.c:107
+#: src/sysfile-info.c:109
 msgid "Little."
 msgstr ""
 
-#: src/sysfile-info.c:108
+#: src/sysfile-info.c:110
 msgid "Variables:"
 msgstr ""
 
-#: src/sysfile-info.c:111
+#: src/sysfile-info.c:113
 msgid "Cases:"
 msgstr ""
 
-#: src/sysfile-info.c:114
+#: src/sysfile-info.c:116
 msgid "Type:"
 msgstr ""
 
-#: src/sysfile-info.c:115
+#: src/sysfile-info.c:117
 msgid "System File."
 msgstr ""
 
-#: src/sysfile-info.c:116
+#: src/sysfile-info.c:118
 msgid "Weight:"
 msgstr ""
 
-#: src/sysfile-info.c:120
+#: src/sysfile-info.c:122
 msgid "Not weighted."
 msgstr ""
 
-#: src/sysfile-info.c:122
+#: src/sysfile-info.c:124
 msgid "Mode:"
 msgstr ""
 
-#: src/sysfile-info.c:124
+#: src/sysfile-info.c:126
 #, c-format
 msgid "Compression %s."
 msgstr ""
 
-#: src/sysfile-info.c:124
+#: src/sysfile-info.c:126
 msgid "on"
 msgstr ""
 
-#: src/sysfile-info.c:124
+#: src/sysfile-info.c:126
 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 ""
 
-#: src/sysfile-info.c:134 src/sysfile-info.c:368
+#: src/sysfile-info.c:136 src/sysfile-info.c:370
 msgid "Position"
 msgstr ""
 
-#: src/sysfile-info.c:191
+#: src/sysfile-info.c:193
 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 ""
 
-#: src/sysfile-info.c:256
+#: src/sysfile-info.c:258
 msgid "No variables to display."
 msgstr ""
 
-#: src/sysfile-info.c:275
+#: src/sysfile-info.c:277
 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 ""
 
-#: src/sysfile-info.c:294
+#: src/sysfile-info.c:296
 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 ""
 
-#: src/sysfile-info.c:444
+#: src/sysfile-info.c:446
 #, c-format
 msgid "Format: %s"
 msgstr ""
 
-#: src/sysfile-info.c:451
+#: src/sysfile-info.c:453
 #, c-format
 msgid "Print Format: %s"
 msgstr ""
 
-#: src/sysfile-info.c:454
+#: src/sysfile-info.c:456
 #, c-format
 msgid "Write Format: %s"
 msgstr ""
 
-#: src/sysfile-info.c:462
+#: src/sysfile-info.c:464
 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 ""
 
-#: src/sysfile-info.c:586
+#: src/sysfile-info.c:588
 msgid "No vectors defined."
 msgstr ""
 
-#: src/sysfile-info.c:601
+#: src/sysfile-info.c:603
 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 ""
 
-#: src/temporary.c:45
+#: src/temporary.c:46
 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 ""
 
-#: src/title.c:55
+#: src/title.c:56
 #, c-format
 msgid "%s before: %s\n"
 msgstr ""
 
-#: src/title.c:55
+#: src/title.c:56
 msgid "<none>"
 msgstr ""
 
-#: src/title.c:67
+#: src/title.c:68
 #, c-format
 msgid "%s: `.' expected after string."
 msgstr ""
 
-#: src/title.c:83
+#: src/title.c:84
 #, 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 ""
@@ -3889,29 +3759,29 @@ 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 ""
 
-#: src/vars-prs.c:100
+#: src/vars-prs.c:101
 msgid "ordinary"
 msgstr ""
 
-#: src/vars-prs.c:102
+#: src/vars-prs.c:103
 msgid "system"
 msgstr ""
 
-#: src/vars-prs.c:104
+#: src/vars-prs.c:105
 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 ""
 
-#: 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 "
@@ -3919,700 +3789,706 @@ msgid ""
 "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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/vars-prs.c:272
+#: src/vars-prs.c:273
 #, 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 ""
 
-#: src/vars-prs.c:399
+#: src/vars-prs.c:400
 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 ""
 
-#: src/vars-prs.c:426
+#: src/vars-prs.c:427
 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 ""
 
-#: src/vector.c:72
+#: src/vector.c:73
 #, 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 ""
 
-#: src/vector.c:127
+#: src/vector.c:128
 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 ""
 
-#: 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 ""
 
-#: src/weight.c:52
+#: src/weight.c:53
 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 ""
 
-#: src/crosstabs.q:241
+#: src/crosstabs.q:261
 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 ""
 
-#: src/crosstabs.q:312
+#: src/crosstabs.q:332
 msgid "expecting BY"
 msgstr ""
 
-#: src/crosstabs.q:379
+#: src/crosstabs.q:399
 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 ""
 
-#: src/crosstabs.q:769
+#: src/crosstabs.q:800
 msgid "Summary."
 msgstr ""
 
-#: src/crosstabs.q:771 src/examine.q:784
+#: src/crosstabs.q:802 src/examine.q:837
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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/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 ""
 
-#: src/crosstabs.q:1027
+#: src/crosstabs.q:1058
 msgid "count"
 msgstr ""
 
-#: src/crosstabs.q:1028
+#: src/crosstabs.q:1059
 msgid "row %"
 msgstr ""
 
-#: src/crosstabs.q:1029
+#: src/crosstabs.q:1060
 msgid "column %"
 msgstr ""
 
-#: src/crosstabs.q:1030
+#: src/crosstabs.q:1061
 msgid "total %"
 msgstr ""
 
-#: src/crosstabs.q:1031
+#: src/crosstabs.q:1062
 msgid "expected"
 msgstr ""
 
-#: src/crosstabs.q:1032
+#: src/crosstabs.q:1063
 msgid "residual"
 msgstr ""
 
-#: src/crosstabs.q:1033
+#: src/crosstabs.q:1064
 msgid "std. resid."
 msgstr ""
 
-#: src/crosstabs.q:1034
+#: src/crosstabs.q:1065
 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 ""
 
-#: 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 ""
 
-#: src/crosstabs.q:1071
+#: src/crosstabs.q:1102
 msgid "Asymp. Sig. (2-sided)"
 msgstr ""
 
-#: src/crosstabs.q:1073
+#: src/crosstabs.q:1104
 msgid "Exact. Sig. (2-sided)"
 msgstr ""
 
-#: src/crosstabs.q:1075
+#: src/crosstabs.q:1106
 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 ""
 
-#: src/crosstabs.q:1096 src/crosstabs.q:1138
+#: src/crosstabs.q:1127 src/crosstabs.q:1169
 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 ""
 
-#: src/crosstabs.q:1098 src/crosstabs.q:1140
+#: src/crosstabs.q:1129 src/crosstabs.q:1171
 msgid "Approx. Sig."
 msgstr ""
 
-#: src/crosstabs.q:1113
+#: src/crosstabs.q:1144
 #, 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 ""
 
-#: 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 ""
 
-#: src/crosstabs.q:1136
+#: src/crosstabs.q:1167
 msgid "Type"
 msgstr ""
 
-#: src/crosstabs.q:1884
+#: src/crosstabs.q:1916
 msgid "Pearson Chi-Square"
 msgstr ""
 
-#: src/crosstabs.q:1885
+#: src/crosstabs.q:1917
 msgid "Likelihood Ratio"
 msgstr ""
 
-#: src/crosstabs.q:1886
+#: src/crosstabs.q:1918
 msgid "Fisher's Exact Test"
 msgstr ""
 
-#: src/crosstabs.q:1887
+#: src/crosstabs.q:1919
 msgid "Continuity Correction"
 msgstr ""
 
-#: src/crosstabs.q:1888
+#: src/crosstabs.q:1920
 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 ""
 
-#: src/crosstabs.q:1941 src/crosstabs.q:2070
+#: src/crosstabs.q:1973 src/crosstabs.q:2102
 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 ""
 
-#: src/crosstabs.q:1943
+#: src/crosstabs.q:1975
 msgid "Interval by Interval"
 msgstr ""
 
-#: src/crosstabs.q:1944
+#: src/crosstabs.q:1976
 msgid "Measure of Agreement"
 msgstr ""
 
-#: src/crosstabs.q:1949
+#: src/crosstabs.q:1981
 msgid "Phi"
 msgstr ""
 
-#: src/crosstabs.q:1950
+#: src/crosstabs.q:1982
 msgid "Cramer's V"
 msgstr ""
 
-#: src/crosstabs.q:1951
+#: src/crosstabs.q:1983
 msgid "Contingency Coefficient"
 msgstr ""
 
-#: src/crosstabs.q:1952
+#: src/crosstabs.q:1984
 msgid "Kendall's tau-b"
 msgstr ""
 
-#: src/crosstabs.q:1953
+#: src/crosstabs.q:1985
 msgid "Kendall's tau-c"
 msgstr ""
 
-#: src/crosstabs.q:1954
+#: src/crosstabs.q:1986
 msgid "Gamma"
 msgstr ""
 
-#: src/crosstabs.q:1955
+#: src/crosstabs.q:1987
 msgid "Spearman Correlation"
 msgstr ""
 
-#: src/crosstabs.q:1956
+#: src/crosstabs.q:1988
 msgid "Pearson's R"
 msgstr ""
 
-#: src/crosstabs.q:1957
+#: src/crosstabs.q:1989
 msgid "Kappa"
 msgstr ""
 
-#: src/crosstabs.q:2027
+#: src/crosstabs.q:2059
 #, 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 ""
 
-#: src/crosstabs.q:2038
+#: src/crosstabs.q:2070
 #, c-format
 msgid "For cohort %s = %g"
 msgstr ""
 
-#: src/crosstabs.q:2041
+#: src/crosstabs.q:2073
 #, c-format
 msgid "For cohort %s = %.*s"
 msgstr ""
 
-#: src/crosstabs.q:2072
+#: src/crosstabs.q:2104
 msgid "Nominal by Interval"
 msgstr ""
 
-#: src/crosstabs.q:2077
+#: src/crosstabs.q:2109
 msgid "Lambda"
 msgstr ""
 
-#: src/crosstabs.q:2078
+#: src/crosstabs.q:2110
 msgid "Goodman and Kruskal tau"
 msgstr ""
 
-#: src/crosstabs.q:2079
+#: src/crosstabs.q:2111
 msgid "Uncertainty Coefficient"
 msgstr ""
 
-#: src/crosstabs.q:2080
+#: src/crosstabs.q:2112
 msgid "Somers' d"
 msgstr ""
 
-#: src/crosstabs.q:2081
+#: src/crosstabs.q:2113
 msgid "Eta"
 msgstr ""
 
-#: src/crosstabs.q:2086
+#: src/crosstabs.q:2118
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/examine.q:563 src/oneway.q:410
+#: src/examine.q:610 src/oneway.q:412
 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 ""
 
-#: src/examine.q:608 src/oneway.q:403
+#: src/examine.q:655 src/oneway.q:405
 msgid "Lower Bound"
 msgstr ""
 
-#: src/examine.q:619 src/oneway.q:404
+#: src/examine.q:666 src/oneway.q:406
 msgid "Upper Bound"
 msgstr ""
 
-#: src/examine.q:631
+#: src/examine.q:678
 msgid "5% Trimmed Mean"
 msgstr ""
 
-#: src/examine.q:636 src/frequencies.q:102
+#: src/examine.q:689 src/frequencies.q:112
 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 ""
 
-#: src/examine.q:701
+#: src/examine.q:754
 msgid "Interquartile Range"
 msgstr ""
 
-#: src/examine.q:778
+#: src/examine.q:831
 msgid "Case Processing Summary"
 msgstr ""
 
-#: src/examine.q:1103
+#: src/examine.q:1157
 msgid "Extreme Values"
 msgstr ""
 
-#: src/examine.q:1126
+#: src/examine.q:1180
 msgid "Case Number"
 msgstr ""
 
-#: src/examine.q:1252
+#: src/examine.q:1306
 msgid "Highest"
 msgstr ""
 
-#: src/examine.q:1257
+#: src/examine.q:1311
 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 ""
 
-#: src/file-handle.q:147
+#: src/file-handle.q:144
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/file-handle.q:262
+#: src/file-handle.q:317
 msgid "expecting a file name or handle name"
 msgstr ""
 
-#: src/frequencies.q:101
+#: src/frequencies.q:111
 msgid "S.E. Mean"
 msgstr ""
 
-#: src/frequencies.q:103
+#: src/frequencies.q:113
 msgid "Mode"
 msgstr ""
 
-#: src/frequencies.q:107
+#: src/frequencies.q:117
 msgid "S.E. Kurt"
 msgstr ""
 
-#: src/frequencies.q:109
+#: src/frequencies.q:119
 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 ""
 
-#: 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 ""
 
-#: src/frequencies.q:722
+#: src/frequencies.q:747
 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 ""
 
-#: src/frequencies.q:747
+#: src/frequencies.q:766
 #, 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 ""
 
-#: src/frequencies.q:822
+#: src/frequencies.q:844
 #, 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 ""
 
-#: 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 ""
 
-#: src/frequencies.q:1057
+#: src/frequencies.q:1084
 msgid "Frequency"
 msgstr ""
 
-#: src/frequencies.q:1076
+#: src/frequencies.q:1104
 msgid "Value Label"
 msgstr ""
 
-#: src/frequencies.q:1175
+#: src/frequencies.q:1205
 msgid "Freq"
 msgstr ""
 
-#: src/frequencies.q:1176 src/frequencies.q:1178
+#: src/frequencies.q:1206 src/frequencies.q:1208
 msgid "Pct"
 msgstr ""
 
-#: src/frequencies.q:1394
+#: src/frequencies.q:1427
 #, c-format
 msgid "No valid data for variable %s; statistics not displayed."
 msgstr ""
 
-#: src/frequencies.q:1433
+#: src/frequencies.q:1465
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/list.q:195
+#: src/list.q:197
 msgid "`/FORMAT WEIGHT' specified, but weighting is not on."
 msgstr ""
 
-#: src/list.q:436
+#: src/list.q:438
 msgid "Line"
 msgstr ""
 
-#: src/means.q:101
+#: src/means.q:100
 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 ""
 
-#: src/oneway.q:166
+#: src/oneway.q:168
 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 ""
 
-#: 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 ""
 
-#: src/oneway.q:275
+#: src/oneway.q:277
 msgid "Sum of Squares"
 msgstr ""
 
-#: src/oneway.q:277
+#: src/oneway.q:279
 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 ""
 
-#: src/oneway.q:279 src/oneway.q:549
+#: src/oneway.q:281 src/oneway.q:552
 msgid "Significance"
 msgstr ""
 
-#: src/oneway.q:304
+#: src/oneway.q:305
 msgid "Between Groups"
 msgstr ""
 
-#: src/oneway.q:305
+#: src/oneway.q:306
 msgid "Within Groups"
 msgstr ""
 
-#: src/oneway.q:351
+#: src/oneway.q:353
 msgid "ANOVA"
 msgstr ""
 
-#: src/oneway.q:546
+#: src/oneway.q:549
 msgid "Levene Statistic"
 msgstr ""
 
-#: src/oneway.q:547
+#: src/oneway.q:550
 msgid "df1"
 msgstr ""
 
-#: src/oneway.q:548
+#: src/oneway.q:551
 msgid "df2"
 msgstr ""
 
-#: src/oneway.q:552
+#: src/oneway.q:555
 msgid "Test of Homogeneity of Variances"
 msgstr ""
 
-#: src/oneway.q:628
+#: src/oneway.q:631
 msgid "Contrast Coefficients"
 msgstr ""
 
-#: src/oneway.q:630 src/oneway.q:707
+#: src/oneway.q:633 src/oneway.q:703
 msgid "Contrast"
 msgstr ""
 
-#: src/oneway.q:705
+#: src/oneway.q:701
 msgid "Contrast Tests"
 msgstr ""
 
-#: src/oneway.q:708
+#: src/oneway.q:704
 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 ""
 
-#: 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 ""
 
-#: src/oneway.q:760
+#: src/oneway.q:754
 msgid "Assume equal variances"
 msgstr ""
 
-#: src/oneway.q:764
+#: src/oneway.q:758
 msgid "Does not assume equal"
 msgstr ""
 
@@ -4748,115 +4624,115 @@ msgstr ""
 msgid "data> "
 msgstr ""
 
-#: src/t-test.q:266
+#: src/t-test.q:268
 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 ""
 
-#: src/t-test.q:320
+#: src/t-test.q:322
 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 ""
 
-#: 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 ""
 
-#: 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 ""
 
-#: src/t-test.q:501
+#: src/t-test.q:503
 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 ""
 
-#: 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 ""
 
-#: src/t-test.q:700
+#: src/t-test.q:702
 msgid "Group Statistics"
 msgstr ""
 
-#: src/t-test.q:824
+#: src/t-test.q:826
 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 ""
 
-#: src/t-test.q:964
+#: src/t-test.q:965
 msgid "Independent Samples Test"
 msgstr ""
 
-#: src/t-test.q:972
+#: src/t-test.q:973
 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 ""
 
-#: src/t-test.q:977 src/t-test.q:1367
+#: src/t-test.q:978 src/t-test.q:1367
 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 ""
 
-#: src/t-test.q:982
+#: src/t-test.q:983
 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 ""
 
-#: src/t-test.q:1041
+#: src/t-test.q:1043
 msgid "Equal variances assumed"
 msgstr ""
 
-#: src/t-test.q:1094
+#: src/t-test.q:1095
 msgid "Equal variances not assumed"
 msgstr ""
 
-#: src/t-test.q:1152
+#: src/t-test.q:1153
 msgid "Paired Samples Test"
 msgstr ""
 
-#: src/t-test.q:1155
+#: src/t-test.q:1156
 msgid "Paired Differences"
 msgstr ""
 
-#: src/t-test.q:1167
+#: src/t-test.q:1168
 msgid "Std. Error Mean"
 msgstr ""
 
-#: src/t-test.q:1246
+#: src/t-test.q:1247
 msgid "One-Sample Test"
 msgstr ""
 
-#: src/t-test.q:1251
+#: src/t-test.q:1252
 #, c-format
 msgid "Test Value = %f"
 msgstr ""
index 87780b84698cbc12af4284e97d647f267dacadff..3f30425b495f324205b7618cde743aeb9e2c3425 100644 (file)
@@ -2,10 +2,12 @@ Makefile
 Makefile.in
 correlations.c
 crosstabs.c
+examine.c
 file-handle.c
 frequencies.c
 list.c
 means.c
+oneway.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 
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     \
-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    \
@@ -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                    \
-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           \
-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                \
index ec601dbffdba49806bb2d2c4c62482a1ae084d41..ffd29166142cb2bd44512ebb80cb17d2b935f7f6 100644 (file)
@@ -24,6 +24,7 @@
 #include "case.h"
 #include "casefile.h"
 #include "command.h"
+#include "dictionary.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 "sfm.h"
+#include "sfm-write.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 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. */
@@ -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. */
-    flt64 *sfm_agr_case;                /* Aggregate case in SFM format. */
   };
 
 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);
-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. */
-static void write_case_to_sfm (struct agr_proc *agr);
 static int presorted_agr_to_sysfile (struct ccase *, void *aux);
 \f
 /* Parsing. */
@@ -158,11 +156,12 @@ int
 cmd_aggregate (void)
 {
   struct agr_proc agr;
+  struct file_handle *out_file = NULL;
 
   /* Have we seen these subcommands? */
   unsigned seen = 0;
 
-  agr.out_file = NULL;
+  agr.writer = 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");
-              goto lossage;
+              goto error;
            }
          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"))
@@ -206,7 +203,7 @@ cmd_aggregate (void)
          if (!lex_match_id ("COLUMNWISE"))
            {
              lex_error (_("while expecting COLUMNWISE"));
-              goto lossage;
+              goto error;
            }
          agr.missing = COLUMNWISE;
        }
@@ -221,7 +218,7 @@ cmd_aggregate (void)
          if (seen & 8)
            {
              msg (SE, _("%s subcommand given multiple times."),"BREAK");
-              goto lossage;
+              goto error;
            }
          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)
-            goto lossage;
+            goto error;
          
           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))
-    goto lossage;
+    goto error;
 
   /* Delete documents. */
   if (!(seen & 2))
@@ -262,7 +259,7 @@ cmd_aggregate (void)
   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. */
@@ -289,9 +286,10 @@ cmd_aggregate (void)
     }
   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. */
@@ -301,12 +299,12 @@ cmd_aggregate (void)
           
           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)) 
-                write_case_to_sfm (&agr);
+                sfm_write_case (agr.writer, &agr.agr_case);
               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);
-          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;
 
-lossage:
+error:
   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)
@@ -390,8 +370,9 @@ parse_aggregate_functions (struct agr_proc *agr)
        {
          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. */
          {
@@ -414,7 +395,7 @@ parse_aggregate_functions (struct agr_proc *agr)
       if (token != T_ID)
        {
          lex_error (_("expecting aggregation function"));
-         goto lossage;
+         goto error;
        }
 
       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);
-         goto lossage;
+         goto error;
        }
       func_index = function - agr_func_tab;
       lex_get ();
@@ -445,7 +426,7 @@ parse_aggregate_functions (struct agr_proc *agr)
          else
            {
              lex_error (_("expecting `('"));
-             goto lossage;
+             goto error;
            }
        } 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))
-             goto lossage;
+             goto error;
          }
 
          /* 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);
-                   goto lossage;
+                   goto error;
                  }
            
                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);
-                   goto lossage;
+                   goto error;
                  }
              }
 
@@ -498,7 +479,7 @@ parse_aggregate_functions (struct agr_proc *agr)
          if (!lex_match(')'))
            {
              lex_error (_("expecting `)'"));
-             goto lossage;
+             goto error;
            }
          
          /* 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);
-             goto lossage;
+             goto error;
            }
        }
        
@@ -584,7 +565,7 @@ parse_aggregate_functions (struct agr_proc *agr)
                           "variables."),
                     dest[i]);
                free (dest[i]);
-               goto lossage;
+               goto error;
              }
 
            free (dest[i]);
@@ -636,7 +617,7 @@ parse_aggregate_functions (struct agr_proc *agr)
        }
       continue;
       
-    lossage:
+    error:
       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;
 
-  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);
+  free (agr->prev_break);
   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);
     }
-  free (agr->prev_break);
+  if (agr->dict != NULL)
+    dict_destroy (agr->dict);
   case_destroy (&agr->agr_case);
 }
 \f
@@ -1143,38 +1125,6 @@ agr_to_active_file (struct ccase *c, void *agr_)
   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
@@ -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)) 
-    write_case_to_sfm (agr);
+    sfm_write_case (agr->writer, &agr->agr_case);
 
   return 1;
 }
index 1aaba30579d4bae63477671a2943c9ed923a9366..a0c2fbac5bbf4a79648cb2663f1bcddc99855228 100644 (file)
@@ -19,9 +19,9 @@
 
 #include <config.h>
 #include "alloc.h"
-#include "error.h"
 #include <stdio.h>
 #include <stdlib.h>
+#include "error.h"
 #include "str.h"
 \f
 /* Public functions. */
index 9f6867cc4ea3da247a04a677dbd9b8733126edab..4cc8c10379644585db7b33f358854e9d6be59c05 100644 (file)
 #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 "sfm.h"
+#include "sfm-read.h"
 #include "str.h"
 #include "value-labels.h"
 #include "var.h"
@@ -36,6 +37,7 @@ int
 cmd_apply_dictionary (void)
 {
   struct file_handle *handle;
+  struct sfm_reader *reader;
   struct dictionary *dict;
 
   int n_matched = 0;
@@ -44,13 +46,14 @@ cmd_apply_dictionary (void)
   
   lex_match_id ("FROM");
   lex_match ('=');
-  handle = fh_parse_file_handle ();
+  handle = fh_parse ();
   if (!handle)
     return CMD_FAILURE;
 
-  dict = sfm_read_dictionary (handle, NULL);
+  reader = sfm_open_reader (handle, &dict, NULL);
   if (dict == NULL)
     return CMD_FAILURE;
+  sfm_close_reader (reader);
 
   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);
     }
   
-  sfm_maybe_close (handle);
+  sfm_close_reader (reader);
 
   return lex_end_of_command ();
 }
index 5bf12bc0a58f70248541f66d026bc33c2a709da1..a28b13bdcac0e8c3cd2a6257f4019de448324a6f 100644 (file)
@@ -23,6 +23,7 @@
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
+#include "dictionary.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;
-  char   *label;
+  const char *label;
 };
 
 static const struct subcat sub_catagory[SUB_CATAGORIES] = 
index a2396abcf765da48037458e03e14c3f5ed2c7f70..34e1a3dd0d50c03ada8c6352772c25bcd75b364b 100644 (file)
@@ -27,7 +27,7 @@
 struct dataset
 {
   int n_data;
-  char *label;
+  const char *label;
 };
 
 
index 6c427df5bc4dedcbdca0413cb62357b2300fe257..21fb0bdbfdb725a58cd73f5640e5ed8c5f9ebee7 100644 (file)
 #ifdef GLOBAL_DEBUGGING
 #undef NDEBUG
 #else
+#ifndef NDEBUG
 #define NDEBUG
 #endif
+#endif
 #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) 
 {
@@ -49,6 +56,8 @@ case_unshare (struct ccase *c)
           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) 
 {
@@ -57,6 +66,7 @@ case_size (size_t value_cnt)
 }
 
 #ifdef GLOBAL_DEBUGGING
+/* Initializes C as a null case. */
 void
 case_nullify (struct ccase *c) 
 {
@@ -66,6 +76,7 @@ case_nullify (struct ccase *c)
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
+/* Returns true iff C is a null case. */
 int
 case_is_null (const struct ccase *c) 
 {
@@ -73,6 +84,9 @@ case_is_null (const struct ccase *c)
 }
 #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) 
 {
@@ -81,6 +95,7 @@ case_create (struct ccase *c, size_t value_cnt)
 }
 
 #ifdef GLOBAL_DEBUGGING
+/* Initializes CLONE as a copy of 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
+/* 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) 
 {
@@ -116,6 +133,7 @@ case_move (struct ccase *dst, struct ccase *src)
 #endif /* GLOBAL_DEBUGGING */
 
 #ifdef GLOBAL_DEBUGGING
+/* Destroys case C. */
 void
 case_destroy (struct ccase *c) 
 {
@@ -134,6 +152,9 @@ case_destroy (struct ccase *c)
 }
 #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) 
 {
@@ -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) 
 {
@@ -164,6 +188,8 @@ case_try_clone (struct ccase *clone, const struct ccase *orig)
 }
 
 #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,
@@ -191,50 +217,51 @@ case_copy (struct ccase *dst, size_t dst_idx,
 #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
-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);
-  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,
-          case_serial_size (c->case_data->value_cnt));
+          c->case_data->value_cnt * sizeof *output);
 }
 #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
-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);
-  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,
-          case_serial_size (c->case_data->value_cnt));
+          c->case_data->value_cnt * sizeof *input);
 }
 #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) 
 {
@@ -249,6 +276,8 @@ case_data (const struct ccase *c, size_t idx)
 #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) 
 {
@@ -263,6 +292,10 @@ case_num (const struct ccase *c, size_t idx)
 #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) 
 {
@@ -277,6 +310,9 @@ case_str (const struct ccase *c, size_t idx)
 #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) 
 {
@@ -291,3 +327,37 @@ case_data_rw (struct ccase *c, size_t idx)
   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 
   {
-    struct case_data *case_data;
+    struct case_data *case_data;        /* Actual data. */
 #if GLOBAL_DEBUGGING
-    struct ccase *this;
+    struct ccase *this;                 /* Detects unauthorized move/copy. */
 #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
@@ -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);
 
-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);
@@ -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);
 
+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
@@ -126,28 +129,22 @@ case_copy (struct ccase *dst, size_t dst_idx,
              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
-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,
-          case_serial_size (c->case_data->value_cnt));
+          c->case_data->value_cnt * sizeof *output);
 }
 
 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,
-          case_serial_size (c->case_data->value_cnt));
+          c->case_data->value_cnt * sizeof *input);
 }
 
 static inline const union value *
index 2ff3a5742f3492ff65c719c38ebb6ebe03c96219..03828b7194b228e85f2c109b352ae6b23dc57126 100644 (file)
@@ -37,7 +37,7 @@
 #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
@@ -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. */
-    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. */
@@ -71,9 +70,9 @@ struct casefile
     /* 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. */
@@ -86,8 +85,8 @@ struct casereader
 
     /* 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. */
   };
 
@@ -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 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. */
@@ -122,8 +121,7 @@ casefile_create (size_t 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;
@@ -133,9 +131,9 @@ casefile_create (size_t value_cnt)
   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;
@@ -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) 
 {
-  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);
 }
 
@@ -325,7 +323,8 @@ flush_buffer (struct casefile *cf)
 {
   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;
@@ -371,7 +370,7 @@ casefile_to_disk (const struct casefile *cf_)
   struct casereader *reader;
   
   assert (cf != NULL);
-  
+
   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->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++)
@@ -482,7 +481,6 @@ static void
 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)
@@ -508,17 +506,17 @@ reader_open_file (struct casereader *reader)
     }
   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
-                            * cf->case_size);
+                            * cf->value_cnt);
     }
   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));
 
-  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);
@@ -536,11 +534,12 @@ reader_open_file (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));
-  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); 
 }
@@ -575,15 +574,15 @@ casereader_read (struct casereader *reader, struct ccase *c)
     }
   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;
         }
 
-      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);
@@ -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
-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)
@@ -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
-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)
@@ -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);
-          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);
         }
index c550edf1ea11c5a68ec806c25444fb1e935ae7aa..34527e41628edd30adc7635181ea9b399901eac8 100644 (file)
@@ -17,7 +17,7 @@
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
    02111-1307, USA. */
 
-
+#include <config.h>
 #include <stdio.h>
 #include <plot.h>
 #include <stdarg.h>
@@ -28,6 +28,7 @@
 #include <math.h>
 
 #include "chart.h"
+#include "str.h"
 
 
 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,
+                    const struct freq_tab *frq_tab,
                    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);
 
 
-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);
 
index 5ff4f0de2c0e746cfac3c81b28cb05e9f6380cb6..5e4e1a5f7c035264a9d37836201fa0973b717119 100644 (file)
@@ -25,6 +25,7 @@
 #include <ctype.h>
 #include <errno.h>
 #include "alloc.h"
+#include "dictionary.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 "dictionary.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 "dictionary.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 ('*'))
-    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;
index a6e70616460cf7ce40583c0f858e5801095d75a7..35021f09e3589d8055c240d528711dcb0ef73d5f 100644 (file)
@@ -23,6 +23,7 @@
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
+#include "dictionary.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 "dictionary.h"
 #include "hash.h"
 #include "pool.h"
 #include "command.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"
 
+/* (headers) */
+
 #include "debug-print.h"
 
 /* (specification)
@@ -105,6 +109,22 @@ struct crosstab
                                   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
   {
@@ -426,11 +446,13 @@ crs_custom_variables (struct cmd_crosstabs *cmd UNUSED)
        }
       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 == '/')
@@ -475,14 +497,14 @@ precalc (void *aux UNUSED)
 
          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);
-         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;
@@ -493,8 +515,9 @@ precalc (void *aux UNUSED)
              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);
@@ -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++)
-               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);
        }
@@ -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];
+          struct var_range *vr = get_var_range (v);
          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;
@@ -627,15 +654,19 @@ calc_integer (struct ccase *c, void *aux UNUSED)
          
          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;
       }
@@ -804,8 +835,8 @@ make_summary_table (void)
       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++)
@@ -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
-   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.
    */
@@ -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)
 {
+  struct variable *v = xtab[(*entries)->table]->vars[var_idx];
+
   if (mode == GENERAL)
     {
-      int width = xtab[(*entries)->table]->vars[var_idx]->width;
+      int width = v->width;
       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
     {
-      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);
-      *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 "dfm.h"
+#include "dfm-read.h"
+#include "dictionary.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 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 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. */
@@ -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 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;
@@ -108,21 +110,19 @@ static trns_proc_func data_list_trns_proc;
 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);
-  dls->handle = default_handle;
+  dls->reader = NULL;
   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;
@@ -132,11 +132,11 @@ cmd_data_list (void)
       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)
-              && 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."));
@@ -149,7 +149,7 @@ cmd_data_list (void)
          lex_match ('(');
          if (!lex_force_int ())
            goto error;
-         dls->nrec = lex_integer ();
+         dls->rec_cnt = lex_integer ();
          lex_get ();
          lex_match (')');
        }
@@ -231,7 +231,7 @@ cmd_data_list (void)
     }
 
   dls->case_size = dict_get_case_size (default_dict);
-  default_handle = dls->handle;
+  default_handle = fh;
 
   if (dls->type == -1)
     dls->type = DLS_FIXED;
@@ -249,17 +249,18 @@ cmd_data_list (void)
       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)
-       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)
@@ -393,14 +394,14 @@ parse_fixed (struct data_list_pgm *dls)
       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;
     }
-  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"));
@@ -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,
-                  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;
-  char *buf;
-  const char *filename;
   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));
     }
 
-  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);
-  local_free (buf);
 }
 \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
-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;
@@ -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));
       }
   }
-  
-  {
-    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);
 }
@@ -972,11 +960,11 @@ cut_field (const struct data_list_pgm *dls, struct len_string *field,
   char *cp;
   size_t column_start;
 
-  if (dfm_eof (dls->handle))
+  if (dfm_eof (dls->reader))
     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) 
@@ -1020,7 +1008,7 @@ cut_field (const struct data_list_pgm *dls, struct len_string *field,
     {
       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'))
@@ -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;
-                  dfm_forward_record (dls->handle);
+                  dfm_forward_record (dls->reader);
                   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;
 }
@@ -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;
 
-  if (dfm_eof (dls->handle))
+  if (dfm_eof (dls->reader))
     return -2;
-  for (i = 1; i <= dls->nrec; i++)
+  for (i = 1; i <= dls->rec_cnt; i++)
     {
       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."),
-              i - 1, dls->nrec);
+              i - 1, dls->rec_cnt);
          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)
        {
@@ -1129,7 +1117,7 @@ read_from_data_list_fixed (const struct data_list_pgm *dls,
          data_in (&di);
        }
 
-      dfm_forward_record (dls->handle);
+      dfm_forward_record (dls->reader);
     }
 
   return -1;
@@ -1157,9 +1145,9 @@ read_from_data_list_free (const struct data_list_pgm *dls,
          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 "
@@ -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;
 
-  if (dfm_eof (dls->handle))
+  if (dfm_eof (dls->reader))
     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;
 }
 
@@ -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);
-  fh_close_handle (dls->handle);
+  dfm_close_reader (dls->reader);
   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;
 
-  dfm_push (dls->handle);
+  dfm_push (dls->reader);
 
   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 ();
-          dfm_pop (dls->handle);
+          dfm_pop (dls->reader);
           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;
     }
   
-  dfm_pop (dls->handle);
+  dfm_pop (dls->reader);
 
   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);
 
-  dfm_push (dls->handle);
+  dfm_push (dls->reader);
   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. */
@@ -1361,7 +1347,7 @@ struct repeating_data_trns
   {
     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. */
@@ -1392,17 +1378,14 @@ int
 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);
-  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;
@@ -1418,11 +1401,12 @@ cmd_repeating_data (void)
     {
       if (lex_match_id ("FILE"))
        {
+          struct file_handle *file;
          lex_match ('=');
-         rpd->handle = fh_parse_file_handle ();
-         if (!rpd->handle)
+         file = fh_parse ();
+         if (file == NULL)
            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."));
@@ -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)
-    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)
-    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)
@@ -1635,7 +1619,7 @@ cmd_repeating_data (void)
     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;
@@ -1939,15 +1923,15 @@ repeating_data_trns_proc (struct trns_header *trns, struct ccase *c,
     
   int skip_first_record = 0;
     
-  dfm_push (t->handle);
+  dfm_push (t->reader);
   
   /* 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;
-  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);
@@ -2037,7 +2021,7 @@ repeating_data_trns_proc (struct trns_header *trns, struct ccase *c,
       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 "
@@ -2045,9 +2029,9 @@ repeating_data_trns_proc (struct trns_header *trns, struct ccase *c,
                 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;
@@ -2065,7 +2049,7 @@ repeating_data_trns_proc (struct trns_header *trns, struct ccase *c,
       occurs_left -= code;
     }
     
-  dfm_pop (t->handle);
+  dfm_pop (t->reader);
 
   /* 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);
-  fh_close_handle (rpd->handle);
+  dfm_close_reader (rpd->reader);
   free (rpd->id_value);
 }
 
index e05c92921614c4e3b11d8499248ad281fbda65ee..e84733acb02380eb33d73dc04b9834951dadf1ae 100644 (file)
@@ -29,6 +29,7 @@
 #include "case.h"
 #include "casefile.h"
 #include "command.h"
+#include "dictionary.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>
-#include "error.h"
+#include "dictionary.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"
@@ -124,6 +125,7 @@ dict_clear (struct dictionary *d)
   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); 
@@ -146,6 +148,19 @@ dict_clear (struct dictionary *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)
@@ -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->aux = NULL;
+  v->aux_dtor = NULL;
 
   /* 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);
 
-  /* 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);
@@ -420,7 +440,7 @@ dict_delete_var (struct dictionary *d, struct variable *v)
     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);
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 "dictionary.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"
+/* (headers) */
 #include "chart.h"
 
 /* (specification)
@@ -125,7 +127,7 @@ void np_plot(const struct metrics *m, const char *varname);
 
 
 /* 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);
 
@@ -152,7 +154,7 @@ cmd_examine(void)
 
   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);
@@ -942,7 +944,7 @@ static int bad_weight_warn = 1;
 
 
 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;
@@ -951,8 +953,6 @@ run_examine(const struct casefile *cf, void *cmd_)
   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 ) 
index 2ddefec79cb46ac85b3418defd24db750044f92a..0432765825c507a66070e6252216a3bfd5dd93fa 100644 (file)
@@ -39,6 +39,7 @@
 #include <stdio.h>
 #include "case.h"
 #include "data-in.h"
+#include "dictionary.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>
+#include "dictionary.h"
 #include "expr.h"
 #include "exprP.h"
 #include "error.h"
index 5f18202a48069eff675c36c0c17d604333ccc5f0..00a79109601465fdf05f69710421aec690dd427e 100644 (file)
 #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 "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
@@ -56,20 +31,16 @@ enum file_handle_mode
     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. */
-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. */
-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 *);
index 96aa9486cd894e18b2c1f5dfb33a3b478458191f..9c3a81a366a4bf5de31e657ba27e6a8ee6a8ae3b 100644 (file)
 #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. */
@@ -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. */
-  };
 
-/* 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);
@@ -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) 
 {
-  struct file_handle_list *iter;
+  struct file_handle *iter;
 
   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;
 }
 
@@ -82,27 +80,26 @@ static struct file_handle *
 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)
-        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);
-            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)
-    if (!strcmp (filename, iter->handle->private->filename))
-      return iter->handle
+    if (!strcmp (filename, iter->filename))
+      return iter; 
 
   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."),
-          tokid, handle->private->filename);
+          tokid, handle->filename);
       return CMD_FAILURE;
     }
 
@@ -153,29 +150,29 @@ cmd_file_handle (void)
   switch (cmd.mode)
     {
     case FH_CHARACTER:
-      handle->private->mode = MODE_TEXT;
+      handle->mode = MODE_TEXT;
       if (cmd.sbc_tabwidth)
-        handle->private->tab_width = cmd.n_tabwidth;
+        handle->tab_width = cmd.n_tabwidth[0];
       else
-        handle->private->tab_width = 4;
+        handle->tab_width = 4;
       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."));
-          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);
-          handle->private->length = 1;
+          handle->length = 1;
        }
       else
-        handle->private->length = cmd.n_lrecl;
+        handle->length = cmd.n_lrecl[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;
-  struct file_handle_list *list;
 
   /* 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;
 }
 
-/* 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 *
-fh_parse_file_handle (void)
+fh_parse (void)
 {
   struct file_handle *handle;
 
@@ -292,14 +347,16 @@ fh_parse_file_handle (void)
 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) 
 {
-  return handle->private->filename;
+  assert (handle != NULL);
+  return handle->filename;
 }
 
 /* Returns the mode of HANDLE. */
@@ -307,7 +364,7 @@ enum file_handle_mode
 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
@@ -316,7 +373,7 @@ size_t
 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
@@ -326,7 +383,7 @@ size_t
 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>
-#include "error.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"
@@ -75,7 +76,7 @@ struct record_type
 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. */
@@ -97,13 +98,14 @@ static void create_col_var (struct col_spec *c);
 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);
-  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;
@@ -133,8 +135,8 @@ cmd_file_type (void)
       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"))
@@ -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;
-  default_handle = fty->handle;
+  default_handle = fh;
 
   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;
 
-  dfm_push (fty->handle);
+  dfm_push (fty->reader);
 
   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;
 
-      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;
@@ -689,13 +692,13 @@ file_type_source_read (struct case_source *source,
          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. */
-      dfm_forward_record (fty->handle);
+      dfm_forward_record (fty->reader);
     }
 
 /*  switch(fty->type)
@@ -706,7 +709,7 @@ file_type_source_read (struct case_source *source,
    default: assert(0);
    } */
 
-  dfm_pop (fty->handle);
+  dfm_pop (fty->reader);
 }
 
 static void
@@ -716,6 +719,7 @@ file_type_source_destroy (struct case_source *source)
   struct record_type *iter, *next;
 
   cancel_transformations ();
+  dfm_close_reader (fty->reader);
   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 "dictionary.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 "dictionary.h"
 #include "hash.h"
 #include "pool.h"
 #include "command.h"
@@ -48,6 +49,7 @@
 #include "vfm.h"
 #include "settings.h"
 #include "chart.h"
+/* (headers) */
 
 #include "debug-print.h"
 
 /* (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
   {
@@ -129,7 +139,6 @@ struct percentile
 
 static void add_percentile (double x) ;
 
-
 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. */
 
-/* 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 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 *);
@@ -233,9 +261,6 @@ internal_cmd_frequencies (void)
   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;
 
@@ -244,14 +269,14 @@ internal_cmd_frequencies (void)
 
   /* 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;
-  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++)
-    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;
@@ -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 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:
            {
@@ -439,15 +464,15 @@ calc (struct ccase *c, void *aux UNUSED)
        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;
-             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
-           v->p.frq.tab.out_of_range += weight;
+           ft->out_of_range += weight;
          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];
+      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;
@@ -485,16 +511,16 @@ precalc (void *aux UNUSED)
               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;
 
-         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];
+      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. */
-      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)
@@ -547,17 +575,16 @@ postcalc (void *aux UNUSED)
        {
          struct chart ch;
          double d[frq_n_stats];
-         struct frequencies_proc *frq = &v->p.frq;
          
          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);
-         draw_histogram(&ch, v_variables[i], "HISTOGRAM",&norm,normal);
+         draw_histogram(&ch, v_variables[i], ft, "HISTOGRAM",&norm,normal);
          chart_finalise(&ch);
        }
 
@@ -568,7 +595,7 @@ postcalc (void *aux UNUSED)
 
          chart_initialise(&ch);
          
-         draw_piechart(&ch, v_variables[i]);
+         draw_piechart(&ch, v_variables[i], ft);
 
          chart_finalise(&ch);
        }
@@ -624,9 +651,9 @@ postprocess_freq_tab (struct variable *v)
   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);
-  ft = &v->p.frq.tab;
 
   /* 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)
 {
-  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
@@ -697,9 +725,6 @@ frq_custom_variables (struct cmd_frequencies *cmd UNUSED)
                        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
@@ -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];
+      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;
        }
-      
-      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 (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
-       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;
 }
 
-/* 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)
@@ -817,19 +840,22 @@ frq_custom_grouped (struct cmd_frequencies *cmd UNUSED)
           }
 
        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;
@@ -1032,9 +1058,10 @@ full_dim (struct tab_table *t, struct outp_driver *d)
 
 /* Displays a full frequency table for variable V. */
 static void
-dump_full (struct variable * v)
+dump_full (struct variable *v)
 {
   int n_categories;
+  struct freq_tab *ft;
   struct freq *f;
   struct tab_table *t;
   int r;
@@ -1067,7 +1094,8 @@ dump_full (struct variable * v)
 
   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);
@@ -1079,14 +1107,14 @@ dump_full (struct variable * v)
                  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;
 
-      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)
@@ -1103,7 +1131,7 @@ dump_full (struct variable * v)
       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;
 
@@ -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,
-                    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++;
     }
@@ -1159,15 +1187,17 @@ condensed_dim (struct tab_table *t, struct outp_driver *d)
 
 /* Display condensed frequency table for variable V. */
 static void
-dump_condensed (struct variable * v)
+dump_condensed (struct variable *v)
 {
   int n_categories;
+  struct freq_tab *ft;
   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);
@@ -1179,12 +1209,12 @@ dump_condensed (struct variable * v)
   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;
 
-      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);
@@ -1192,12 +1222,12 @@ dump_condensed (struct variable * v)
       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,
-                f->c / v->p.frq.tab.total_cases * 100.0, 3, 0);
+                f->c / ft->total_cases * 100.0, 3, 0);
       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
-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;
@@ -1255,10 +1286,10 @@ calc_stats (struct variable * v, double d[frq_n_stats])
     }
 
   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;
-      f = &v->p.frq.tab.valid[idx]; 
+      f = &ft->valid[idx]; 
       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 = 
-             (v->p.frq.tab.valid_cases - 1) *  percentiles[i].p;
+             (ft->valid_cases - 1) *  percentiles[i].p;
          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 ) 
            {
@@ -1312,17 +1343,17 @@ calc_stats (struct variable * v, double d[frq_n_stats])
 
   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 ) 
        {
-         s = modf((ft->valid_cases - 1) *  percentiles[i].p , &dummy);
+         s = modf((ft->valid_cases - 1) * percentiles[i].p , &dummy);
        }
       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 + 
@@ -1336,7 +1367,7 @@ calc_stats (struct variable * v, double d[frq_n_stats])
   /* 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) 
         {
@@ -1353,17 +1384,17 @@ calc_stats (struct variable * v, double d[frq_n_stats])
 
   /* 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);
-  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_. */
-  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;
@@ -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
-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;
@@ -1389,7 +1421,8 @@ dump_statistics (struct variable * v, int show_varname)
 
   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);
@@ -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_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++) 
index dc16d7a5372328482cd71d210ad9ff6a00872b41..8f98dea460821bc30578e1153cdd1eb45390ce26 100644 (file)
--- a/src/get.c
+++ b/src/get.c
 #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 "pfm.h"
+#include "pfm-read.h"
+#include "pfm-write.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 "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)
 {
-  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 ('=');
+  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;
 
-  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;
+
+ 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->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);
 
-  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)
 {
-  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)
 {
-  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) 
 {
-  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. */
@@ -246,33 +294,39 @@ save_trns_proc (struct trns_header *h, struct ccase *c, int case_num UNUSED)
 
 /* 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?  Should we call dict_compact_values() on dict as a
-   final step? */
 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())
-    *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;
@@ -288,12 +342,12 @@ trim_dictionary (struct dictionary *dict, int *options)
       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;
@@ -342,8 +396,8 @@ trim_dictionary (struct dictionary *dict, int *options)
          return 0;
        }
 
-      if (*options & GTSV_OPT_MATCH_FILES)
-       return 1;
+      if (op == OP_MATCH)
+        goto success;
     }
 
   if (token != '.')
@@ -351,13 +405,16 @@ trim_dictionary (struct dictionary *dict, int *options)
       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
-rename_variables (struct dictionary * dict)
+rename_variables (struct dictionary *dict)
 {
   int i;
 
@@ -444,40 +501,88 @@ done:
   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. */
 
@@ -499,7 +604,8 @@ struct mtf_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=. */
@@ -534,12 +640,16 @@ static int mtf_processing (struct ccase *, void *);
 
 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;
+  struct mtf_file *iter;
   
   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."));
-             goto lossage;
+             goto error;
            }
          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);
-         goto lossage;
+         goto error;
        }
       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"))
@@ -595,6 +700,15 @@ cmd_match_files (void)
          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)
            {
@@ -624,13 +738,14 @@ cmd_match_files (void)
          
          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."));
-                 goto lossage;
+                 goto error;
                }
              seen |= 2;
 
@@ -639,7 +754,7 @@ cmd_match_files (void)
                {
                  msg (SE, _("Cannot specify the active file since no active "
                             "file has been defined."));
-                 goto lossage;
+                 goto error;
                }
 
               if (temporary != 0)
@@ -650,25 +765,21 @@ cmd_match_files (void)
                          "Temporary transformations will be made permanent."));
                   cancel_temporary (); 
                 }
+
+              file->dict = default_dict;
            }
          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));
            }
-         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)
@@ -681,7 +792,7 @@ cmd_match_files (void)
            {
              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"))
@@ -709,7 +820,7 @@ cmd_match_files (void)
          if (token != T_ID)
            {
              lex_error (NULL);
-             goto lossage;
+             goto error;
            }
 
          if (*name)
@@ -717,7 +828,7 @@ cmd_match_files (void)
              msg (SE, _("Multiple %s subcommands for a single FILE or "
                         "TABLE."),
                   sbc);
-             goto lossage;
+             goto error;
            }
          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);
-             goto lossage;
+             goto error;
            }
        }
       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."));
-             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"))
        {
@@ -753,26 +862,27 @@ cmd_match_files (void)
       else
        {
          lex_error (NULL);
-         goto lossage;
+         goto error;
        }
     }
   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."));
-         goto lossage;
+         goto error;
        }
     }
 
   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;
          
@@ -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);
-                 goto lossage;
+                 goto error;
                }
            }
        }
@@ -852,7 +962,7 @@ cmd_match_files (void)
   mtf_free (&mtf);
   return CMD_SUCCESS;
   
-lossage:
+error:
   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)
 {
-  fh_close_handle (file->handle);
-  if (file->dict != NULL && file->dict != default_dict)
-    dict_destroy (file->dict);
   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);
 }
 
@@ -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);
-        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;
@@ -977,7 +1086,7 @@ mtf_read_nonactive_records (void *mtf_ UNUSED)
     {
       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;
@@ -1121,7 +1230,7 @@ mtf_processing (struct ccase *c, void *mtf_ UNUSED)
            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;
@@ -1149,14 +1258,14 @@ mtf_processing (struct ccase *c, void *mtf_ UNUSED)
               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;
-              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);
-              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
@@ -1176,11 +1285,11 @@ mtf_processing (struct ccase *c, void *mtf_ UNUSED)
              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;
-              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
@@ -1202,9 +1311,9 @@ mtf_processing (struct ccase *c, void *mtf_ UNUSED)
        {
          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);
            }
 
@@ -1218,8 +1327,7 @@ mtf_processing (struct ccase *c, void *mtf_ UNUSED)
   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)
 {
@@ -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;
          }
-       dv->p.mtf.master = mv;
+        set_master (dv, mv);
       }
   }
 
   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. */
 
+/* IMPORT input program. */
+struct import_pgm 
+  {
+    struct pfm_reader *reader;  /* Portable file reader. */
+    struct case_map *map;       /* Map from system file to active file dict. */
+    struct ccase bounce;        /* Bounce buffer. */
+  };
+
+static void import_pgm_free (struct import_pgm *);
+
 /* Parses the IMPORT command. */
 int
 cmd_import (void)
 {
-  struct 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;
 
+  pgm = xmalloc (sizeof *pgm);
+  pgm->reader = NULL;
+  pgm->map = NULL;
+  case_nullify (&pgm->bounce);
+
   for (;;)
     {
       lex_match ('/');
@@ -1313,8 +1451,8 @@ cmd_import (void)
        {
          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"))
@@ -1341,41 +1479,76 @@ cmd_import (void)
 
   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;
-
-  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;
 
-  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;
+
+ 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,
-                    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 =
@@ -1383,85 +1556,128 @@ const struct case_source_class import_source_class =
     "IMPORT",
     NULL,
     import_source_read,
-    get_source_destroy,
+    import_source_destroy,
   };
+
 \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 "dictionary.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;
 
-  /* file-handle.h */
-  fh_init_files ();
-  
   get_date ();
 }
 
index e394745e96496d6d9fae5f8f5d8c1ee01d6f21a1..06e303e559cd3cf16083efd78659d02c4733cb60 100644 (file)
    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 <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 
@@ -48,7 +53,17 @@ hash_group(const struct group_statistics *g, int width)
 
 
 void  
-free_group(struct group_statistics *v, void *aux)
+free_group(struct group_statistics *v, void *aux UNUSED)
 {
   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
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,
+               const struct freq_tab *frq_tab,
               const char *title, 
               struct normal_curve *norm,
               int show_normal)
@@ -96,8 +97,6 @@ draw_histogram(struct chart *ch,
 
   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;
index 0cbf66f325569d54b828c98eac639c2124ac029b..e27b9dee83bb02bc94b8c96c0eb201793369512e 100644 (file)
@@ -25,7 +25,8 @@
 #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"
@@ -312,7 +313,7 @@ struct reread_trns
   {
     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. */
   };
 
@@ -320,16 +321,11 @@ struct reread_trns
 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 != '.')
     {
@@ -351,8 +347,8 @@ cmd_reread (void)
       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;
@@ -369,7 +365,7 @@ cmd_reread (void)
   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);
 
@@ -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)
-    dfm_reread_record (t->handle, 1);
+    dfm_reread_record (t->reader, 1);
   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."));
-         dfm_reread_record (t->handle, 1);
+         dfm_reread_record (t->reader, 1);
        }
       else
-       dfm_reread_record (t->handle, column.f);
+       dfm_reread_record (t->reader, column.f);
     }
   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. */
index 0e36b87a8c12b59817c944be0e14bf14430cb2f6..11e7480acfc115f436d6a81e6d7c336a933f5352 100644 (file)
@@ -24,6 +24,8 @@
 #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"
@@ -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];
+      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;
-      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 = hsh_next(var->p.grp_data.group_hash, &hi))
+           gs = hsh_next(gp->group_hash, &hi))
        {
          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];
+      struct group_proc *gp = group_proc_get (var);
       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 ;
@@ -271,7 +275,7 @@ levene2_precalc (void *_l)
       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);
@@ -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;
 
-      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;
@@ -351,7 +355,8 @@ levene2_postcalc (void *_l)
       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 ;
@@ -359,12 +364,11 @@ levene2_postcalc (void *_l)
        {
          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 "dictionary.h"
 #include "lexer.h"
 #include "error.h"
 #include "magic.h"
@@ -36,6 +37,7 @@
 #include "var.h"
 #include "vfm.h"
 #include "format.h"
+/* (headers) */
 
 #include "debug-print.h"
 
index 5e3f16991393149b27e9f136a921c8cd0db1763f..ebe5ed2257a1f9eaff83b804b8a5b9933296281e 100644 (file)
@@ -22,6 +22,7 @@
 #include "alloc.h"
 #include "case.h"
 #include "command.h"
+#include "dictionary.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 "dictionary.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>
@@ -100,6 +102,7 @@ static int
 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.
@@ -123,6 +126,9 @@ execute_command (void)
   /* 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;
 }
 
index e90aef0955ed5872509a27a4a7726912320346df..a699738ddb04ce8ddbc38d847e371f60fed306bd 100644 (file)
@@ -27,7 +27,8 @@
 #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"
 /* 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
   {
@@ -102,7 +116,7 @@ static const char *content_names[PROX + 1] =
 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. */
@@ -133,21 +147,30 @@ struct matrix_data_pgm
                                    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 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 *);
+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;
-  
+  struct file_handle *fh = NULL;
+    
   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;
-  mx->data_file = inline_file;
+  mx->reader = NULL;
   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);
-                   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]);
@@ -226,18 +248,15 @@ cmd_matrix_data (void)
            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 ('=');
-         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"))
@@ -296,10 +315,9 @@ cmd_matrix_data (void)
 
              mx->single_split = dict_create_var_assert (default_dict,
                                                          tokid, 0);
+              attach_mxd_aux (mx->single_split, MXD_CONTINUOUS, 0);
              lex_get ();
 
-             mx->single_split->p.mxd.vartype = MXD_CONTINUOUS;
-
               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++)
               {
-               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;
                  }
-               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++)
              {
-               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;
                  }
-               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_. */
-  {
-    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. */
@@ -550,7 +570,7 @@ cmd_matrix_data (void)
     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);
   }
@@ -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);
-       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];
@@ -590,7 +611,8 @@ cmd_matrix_data (void)
       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)
@@ -598,6 +620,8 @@ cmd_matrix_data (void)
   else
     read_matrices_without_rowtype (mx);
 
+  dfm_close_reader (mx->reader);
+
   pool_destroy (mx->container);
 
   return CMD_SUCCESS;
@@ -654,20 +678,34 @@ string_to_content_type (char *s, int *collide)
   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
-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_;
-  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
-    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. */
@@ -688,10 +726,10 @@ struct matrix_token
     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
-#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)
@@ -711,28 +749,28 @@ mdump_token (const struct matrix_token *token)
 }
 
 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
 
-/* Return the current position in DATA_FILE. */
+/* Return the current position in READER. */
 static const char *
-context (struct file_handle *data_file)
+context (struct dfm_reader *reader)
 {
   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;
       
-      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++;
@@ -759,16 +797,16 @@ context (struct file_handle *data_file)
 
 /* 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;
       
-      if (dfm_eof (data_file))
+      if (dfm_eof (reader))
         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))
@@ -776,27 +814,27 @@ another_token (struct file_handle *data_file)
 
       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;
         }
 
-      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
-(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;
 
-  if (!another_token (data_file))
+  if (!another_token (reader))
     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);
@@ -856,22 +894,22 @@ static int
        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
-   DATA_FILE. */
+   READER. */
 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;
 
-  if (dfm_eof (data_file))
+  if (dfm_eof (reader))
     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))
@@ -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."),
-          context (data_file), content);
+          context (reader), content);
       return 0;
     }
   
-  dfm_forward_record (data_file);
+  dfm_forward_record (reader);
   return 1;
 }
 \f
@@ -932,8 +970,6 @@ read_matrices_without_rowtype (struct matrix_data_pgm *mx)
 
   free (nr.split_values);
   free (nr.factor_values);
-
-  fh_close_handle (mx->data_file);
 }
 
 /* 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;
-           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,
-                     context (mx->data_file));
+                     context (mx->reader));
                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"));
       }
@@ -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
-          || !another_token (mx->data_file))
+          || !another_token (mx->reader))
        return;
     }
 }
@@ -1212,9 +1248,11 @@ nr_read_splits (struct nr_aux_data *nr, int compare)
 
   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;
     }
 
@@ -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;
-      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."),
-               context (mx->data_file));
+               context (mx->reader));
           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;
-       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."),
-                context (mx->data_file));
+                context (mx->reader));
            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],
-                mx->factors[i]->name, context (mx->data_file));
+                mx->factors[i]->name, context (mx->reader));
            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 *, 
-                            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 *,
@@ -1461,7 +1499,6 @@ read_matrices_with_rowtype (struct matrix_data_pgm *mx)
   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. */
@@ -1485,7 +1522,7 @@ matrix_data_read_with_rowtype (struct case_source *source,
       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);
 }
@@ -1520,12 +1557,12 @@ wr_read_splits (struct wr_aux_data *wr,
     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."),
-                context (mx->data_file));
+                context (mx->reader));
            return 0;
          }
 
@@ -1680,22 +1717,22 @@ wr_output_data (struct wr_aux_data *wr,
   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,
-                 struct file_handle *data_file)
+                 struct dfm_reader *reader)
 {
   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."),
-           context (data_file));
+           context (reader));
       return 0;
     }
   
@@ -1714,7 +1751,7 @@ wr_read_rowtype (struct wr_aux_data *wr,
 
   if (wr->content == -1)
     {
-      msg (SE, _("Syntax error %s."), context (data_file));
+      msg (SE, _("Syntax error %s."), context (reader));
       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;
-       if (!mget_token (&token, mx->data_file))
+       if (!mget_token (&token, mx->reader))
          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;
-           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."),
-                context (mx->data_file));
+                context (mx->reader));
            goto lossage;
          }
        
@@ -1758,9 +1795,9 @@ wr_read_factors (struct wr_aux_data *wr)
   if (wr->content == -1)
     {
       struct matrix_token token;
-      if (!mget_token (&token, mx->data_file))
+      if (!mget_token (&token, mx->reader))
        goto lossage;
-      if (!wr_read_rowtype (wr, &token, mx->data_file))
+      if (!wr_read_rowtype (wr, &token, mx->reader))
        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;
-       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,
-                 context (mx->data_file));
+                 context (mx->reader));
            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"));
   }
@@ -1959,3 +1996,4 @@ matrix_data_without_rowtype_source_class =
     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 "dictionary.h"
 #include "error.h"
 #include "alloc.h"
 #include "command.h"
@@ -35,8 +36,6 @@
 /* (specification)
    means (mns_):
      *tables=custom;
-     +variables=custom;
-     +crossbreak=custom;
      +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 ('=');
 
-  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;
     }
 
-  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
@@ -158,48 +154,6 @@ mns_custom_tables (struct cmd_means *cmd)
 
       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));
 
@@ -211,98 +165,6 @@ mns_custom_tables (struct cmd_means *cmd)
   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
index aab05c6e0e029083241123c0cfa3e19e5c82cc89..5cddfe15a349e3640e0a17e26d5a32de84871148 100644 (file)
@@ -24,6 +24,7 @@
 #include "alloc.h"
 #include "bitvector.h"
 #include "command.h"
+#include "dictionary.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 "dictionary.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 "dictionary.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"
+/* (headers) */
 
 /* (specification)
    "ONEWAY" (oneway_):
@@ -195,7 +197,7 @@ output_oneway(void)
   /* 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);
     }
@@ -281,8 +283,8 @@ show_anova_table(void)
 
   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;
@@ -308,12 +310,13 @@ show_anova_table(void)
        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 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;
        
-       vars[i]->p.grp_data.mse  = (sst - ssa) / df2;
+       gp->mse  = (sst - ssa) / df2;
        
        
        /* 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);
-       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);
@@ -371,7 +374,7 @@ show_descriptives(void)
 
 
   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);
@@ -414,16 +417,17 @@ show_descriptives(void)
     {
       double T;
       double std_error;
-
+      
+      struct group_proc *gp = group_proc_get (vars[v]);
 
       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]);
 
-      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);
@@ -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); 
 
-      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];
+      const struct group_proc *gp = group_proc_get (vars[v]);
       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);
 
-      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);
@@ -716,7 +721,7 @@ show_contrast_tests(short *bad_contrast)
          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;
@@ -796,7 +801,7 @@ show_contrast_tests(short *bad_contrast)
                     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, 
@@ -882,13 +887,14 @@ precalc ( struct cmd_oneway *cmd UNUSED )
 
   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 */
 
-      vars[i]->p.grp_data.group_hash = 
+      gp->group_hash = 
        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);
 
-         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;
 
@@ -985,7 +992,7 @@ run_oneway(const struct casefile *cf, void *cmd_)
          
          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;
@@ -1008,7 +1015,7 @@ run_oneway(const struct casefile *cf, void *cmd_)
                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) 
     {
-      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;
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>.
+   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
@@ -18,7 +20,7 @@
    02111-1307, USA. */
 
 #include <config.h>
-#include "pfm.h"
+#include "pfm-read.h"
 #include "error.h"
 #include <stdarg.h>
 #include <stdio.h>
@@ -28,6 +30,7 @@
 #include <math.h>
 #include "alloc.h"
 #include "case.h"
+#include "dictionary.h"
 #include "file-handle.h"
 #include "format.h"
 #include "getline.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 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. */
   };
 
-static struct fh_ext_class pfm_r_class;
-
 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
-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];
   
   {
@@ -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);
-    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: "),
-            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);
@@ -102,200 +102,173 @@ corrupt_msg (struct file_handle *h, const char *format, ...)
 }
 
 /* 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."),
-         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. */
-#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
-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;
     
-    c = getc (ext->file);
+    c = getc (r->file);
     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')
-      ungetc (c, ext->file);
+      ungetc (c, r->file);
   }
   
-  if (ext->trans)
+  if (r->trans)
     {
       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;
 
lossage:
error:
   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;
-  ext->cc = *ext->bp++;
+  r->cc = *r->bp++;
   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_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;
     }
lossage:
error:
   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. */
-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."),
-           handle_get_filename (h), strerror (errno));
+           handle_get_filename (r->fh), strerror (errno));
       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 ();
 
-  /* 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 */))
-    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 */))
-    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
-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;
@@ -316,7 +289,7 @@ read_float (struct file_handle *h)
 
   for (;;)
     {
-      if (ext->cc >= 64 /* 0 */ && ext->cc <= 93 /* T */)
+      if (r->cc >= 64 /* 0 */ && r->cc <= 93 /* T */)
        {
          got_digit++;
 
@@ -331,14 +304,14 @@ read_float (struct file_handle *h)
               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;
        }
-      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
@@ -349,24 +322,24 @@ read_float (struct file_handle *h)
     }
 
   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;
-      int neg_exp = ext->cc == 141 /* - */;
+      int neg_exp = r->cc == 141 /* - */;
 
       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;
-         exp = exp * 30 + (ext->cc - 64);
+         exp = exp * 30 + (r->cc - 64);
        }
 
       /* 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 /* / */))
-    lose ((h, _("Missing numeric terminator.")));
+    lose ((r, _("Missing numeric terminator.")));
 
   /* 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;
 
lossage:
error:
   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)
-    goto lossage;
+    goto error;
   if (floor (f) != f || f >= INT_MAX || f <= INT_MIN)
-    lose ((h, _("Bad integer format.")));
+    lose ((r, _("Bad integer format.")));
   return f;
 
lossage:
error:
   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 *
-read_string (struct file_handle *h)
+read_string (struct pfm_reader *r)
 {
-  struct pfm_fhuser_ext *ext = h->ext;
   static char *buf;
   int n;
   
-  if (h == NULL)
+  if (r == NULL)
     {
       free (buf);
       buf = NULL;
@@ -440,18 +412,18 @@ read_string (struct file_handle *h)
   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)
-    lose ((h, _("Bad string length %d."), n));
+    lose ((r, _("Bad string length %d."), n));
   
   {
     int i;
 
     for (i = 0; i < n; i++)
       {
-       buf[i] = ext->cc;
+       buf[i] = r->cc;
        advance ();
       }
   }
@@ -459,16 +431,14 @@ read_string (struct file_handle *h)
   buf[n] = 0;
   return buf;
 
lossage:
error:
   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;
@@ -484,7 +454,7 @@ read_header (struct file_handle *h)
 
     for (i = 0; i < 256; i++)
       {
-       src[i] = (unsigned char) ext->cc;
+       src[i] = (unsigned char) r->cc;
        advance ();
       }
 
@@ -498,14 +468,14 @@ read_header (struct file_handle *h)
       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++)
-      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++)
-      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]))
-       lose ((h, "Missing SPSSPORT signature."));
+       lose ((r, "Missing SPSSPORT signature."));
   }
 
   return 1;
 
lossage:
error:
   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 */))
-    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};
-    char *date = read_string (h);
+    char *date = read_string (r);
     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 */)
-         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};
-    char *time = read_string (h);
+    char *time = read_string (r);
     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 */)
-         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;
       
-      product = read_string (h);
+      product = read_string (r);
       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 = read_string (h);
+      subproduct = read_string (r);
       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;
   
lossage:
error:
   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)
-    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)
-    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))
-    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;
 
lossage:
error:
   return 0;
 }
 
@@ -659,52 +627,49 @@ asciify (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_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 */))
-    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. */
   {
-    int x = read_int (h);
+    int x = read_int (r);
 
     if (x == NOT_INT)
-      goto lossage;
+      goto error;
     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 */))
     {
-      weight_name = read_string (h);
+      weight_name = read_string (r);
       if (!weight_name)
-       goto lossage;
+       goto error;
 
       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';
         }
     }
   
-  for (i = 0; i < ext->nvars; i++)
+  for (i = 0; i < r->var_cnt; i++)
     {
       int width;
       unsigned char *name;
@@ -713,23 +678,23 @@ read_variables (struct file_handle *h)
       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)
-       goto lossage;
+       goto error;
       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)
-       goto lossage;
+       goto error;
       for (j = 0; j < 6; j++)
        {
-         fmt[j] = read_int (h);
+         fmt[j] = read_int (r);
          if (fmt[j] == NOT_INT)
-           goto lossage;
+           goto error;
        }
 
       /* 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)
-       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 /* @ */)
-       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 */)
        {
-         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 */;
@@ -758,7 +723,7 @@ read_variables (struct file_handle *h)
 
          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 */;
@@ -768,42 +733,43 @@ read_variables (struct file_handle *h)
                   || 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)
-       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)
-       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;
-         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;
-         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;
-         if (!parse_value (h, &v->missing[0], v))
-           goto lossage;
+         if (!parse_value (r, &v->missing[0], v))
+           goto error;
        }
 
       /* 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)
-           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);
-         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 */))
        {
-         char *label = read_string (h);
+         char *label = read_string (r);
          
          if (label == NULL)
-           goto lossage;
+           goto error;
 
          v->label = xstrdup (label);
          asciify (v->label);
@@ -844,29 +810,29 @@ read_variables (struct file_handle *h)
 
   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)
-        lose ((h, _("Weighting variable %s not present in dictionary."),
+        lose ((r, _("Weighting variable %s not present in dictionary."),
                weight_name));
       free (weight_name);
 
-      dict_set_weight (ext->dict, weight_var);
+      dict_set_weight (dict, weight_var);
     }
 
   return 1;
 
lossage:
error:
   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)
     {
-      char *mv = read_string (h);
+      char *mv = read_string (r);
       int j;
       
       if (mv == NULL)
@@ -882,7 +848,7 @@ parse_value (struct file_handle *h, union value *v, struct variable *vv)
     }
   else
     {
-      v->f = read_float (h);
+      v->f = read_float (r);
       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
-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;
@@ -905,31 +869,31 @@ read_value_label (struct file_handle *h)
 
   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++)
     {
-      char *name = read_string (h);
+      char *name = read_string (r);
       if (name == NULL)
-       goto lossage;
+       goto error;
       asciify (name);
 
-      v[i] = dict_lookup_var (ext->dict, name);
+      v[i] = dict_lookup_var (dict, name);
       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)
-       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));
     }
 
-  n_labels = read_int (h);
+  n_labels = read_int (r);
   if (n_labels == NOT_INT)
-    goto lossage;
+    goto error;
 
   for (i = 0; i < n_labels; i++)
     {
@@ -938,12 +902,12 @@ read_value_label (struct file_handle *h)
 
       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)
-       goto lossage;
+       goto error;
       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)
-           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
-           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;
 
lossage:
error:
   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
-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. */
-  if (ext->cc == 99 /* Z */)
+  if (r->cc == 99 /* Z */)
     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
-       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:
-  lose ((h, _("End of file midway through case.")));
+  lose ((r, _("End of file midway through case.")));
 
- lossage:
-  local_free (temp);
+ error:
   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>
-#include "pfm.h"
+#include "pfm-write.h"
 #include "error.h"
 #include <ctype.h>
 #include <errno.h>
@@ -28,6 +28,8 @@
 #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 "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 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. */
-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. */
-  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."),
-           handle_get_filename (handle), strerror (errno));
+           handle_get_filename (fh), strerror (errno));
       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
-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_;
-  struct pfm_fhuser_ext *ext = h->ext;
 
   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;
-      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;
 
- lossage:
-  abort ();
+ error:
   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
-write_float (struct file_handle *h, double d)
+write_float (struct pfm_writer *w, double d)
 {
   int neg = 0;
   char *mantissa;
@@ -191,7 +175,7 @@ write_float (struct file_handle *h, double d)
     }
   
   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. */
   {
@@ -238,7 +222,7 @@ write_float (struct file_handle *h, double d)
     }
   *cp++ = '/';
   
-  success = bufwrite (h, buf, cp - buf);
+  success = buf_write (w, buf, cp - buf);
   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_int (struct file_handle *h, int n)
+write_int (struct pfm_writer *w, int n)
 {
   char buf[64];
   char *bp = &buf[64];
@@ -277,27 +261,27 @@ write_int (struct file_handle *h, int n)
   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_string (struct file_handle *h, const char *s)
+write_string (struct pfm_writer *w, const char *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
-write_header (struct file_handle *h)
+write_header (struct pfm_writer *w)
 {
   /* 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;
   }
   
@@ -312,11 +296,11 @@ write_header (struct file_handle *h)
        "0000000000000000000000000000000000000000000000000000000000000000"
       };
 
-    if (!bufwrite (h, spss2ascii, 256))
+    if (!buf_write (w, spss2ascii, 256))
       return 0;
   }
 
-  if (!bufwrite (h, "SPSSPORT", 8))
+  if (!buf_write (w, "SPSSPORT", 8))
     return 0;
 
   return 1;
@@ -324,9 +308,9 @@ write_header (struct file_handle *h)
 
 /* 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;
   
   {
@@ -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);
-    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. */
-  if (!bufwrite (h, "1", 1) || !write_string (h, version))
+  if (!buf_write (w, "1", 1) || !write_string (w, version))
     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;
@@ -365,31 +349,31 @@ write_version_data (struct file_handle *h)
 
 /* 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 (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)
-    return write_float (h, v->f);
+    return write_float (w, v->f);
   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_variables (struct file_handle *h, struct dictionary *dict)
+write_variables (struct pfm_writer *w, const struct dictionary *dict)
 {
   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++)
@@ -404,17 +388,17 @@ write_variables (struct file_handle *h, struct dictionary *dict)
 
       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++)
-       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;
 
-      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;
     }
 
@@ -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 (struct file_handle *h, struct dictionary *dict)
+write_value_labels (struct pfm_writer *w, const struct dictionary *dict)
 {
   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 (!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)) 
-       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; 
@@ -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 
-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;
   
-  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
        {
-         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;
        }
     }
@@ -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. */
-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. */
 
 
+#include <config.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 :) */
+#ifndef M_PI
 #define M_PI ( 22.0 / 7.0 ) 
+#endif
 
 
 #define min(A,B) ((A>B)?B:A)
@@ -45,12 +49,11 @@ draw_segment(struct chart *ch,
 
 /* 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;
 
-  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;
index 1bbc5ae6f8df07af94ed6cc0cfd94cd9b07ee7f5..b2ef755cf11bc3fde5f53b158d0e65e0f49604f2 100644 (file)
@@ -25,7 +25,7 @@
 #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"
@@ -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_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;
-    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. */
@@ -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 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. */
@@ -132,16 +133,14 @@ cmd_write (void)
 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;
-  prt.handle = NULL;
+  prt.writer = NULL;
   prt.options = f;
   prt.spec = NULL;
   prt.line = NULL;
@@ -157,16 +156,16 @@ internal_cmd_print (int f)
        {
          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 ())
-           goto lossage;
+           goto error;
          nrec = lex_integer ();
          lex_get ();
          lex_match (')');
@@ -178,20 +177,27 @@ internal_cmd_print (int f)
       else
        {
          lex_error (_("expecting a valid subcommand"));
-         goto lossage;
+         goto error;
        }
     }
 
   /* 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)
-    dump_table ();
+    dump_table (fh);
 
   /* Count the maximum line width.  Allocate linebuffer if
      applicable. */
@@ -204,7 +210,7 @@ internal_cmd_print (int f)
 
   return CMD_SUCCESS;
 
lossage:
error:
   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
-dump_table (void)
+dump_table (const struct file_handle *fh)
 {
   struct prt_out_spec *spec;
   struct tab_table *t;
@@ -831,9 +837,9 @@ dump_table (void)
        assert (0);
       }
 
-  if (prt.handle != NULL)
+  if (fh != NULL)
     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);
@@ -911,15 +917,14 @@ print_trns_proc (struct trns_header * trns, struct ccase * c,
   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:
-       if (t->handle == NULL)
+       if (t->writer == NULL)
          {
            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
-               || handle_get_mode (t->handle) != MODE_BINARY)
+                || !(t->options & PRT_BINARY))
              {
                /* PORTME: Line ends. */
 #ifdef __MSDOS__
@@ -936,7 +941,7 @@ print_trns_proc (struct trns_header * trns, struct ccase * c,
                buf[len++] = '\n';
              }
 
-           dfm_put_record (t->handle, buf, len);
+           dfm_put_record (t->writer, buf, len);
          }
 
        memset (buf, ' ', t->max_width);
@@ -1003,7 +1008,7 @@ struct print_space_trns
 {
   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;
@@ -1015,20 +1020,21 @@ int
 cmd_print_space (void)
 {
   struct print_space_trns *t;
-  struct file_handle *handle;
+  struct file_handle *fh;
   struct expression *e;
+  struct dfm_writer *writer;
 
   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
-    handle = NULL;
+    fh = NULL;
 
   if (token != '.')
     {
@@ -1043,19 +1049,25 @@ cmd_print_space (void)
   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->handle = handle;
+  t->writer = writer;
   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;
 
-  if (t->handle == NULL)
+  if (t->writer == NULL)
     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--)
-       dfm_put_record (t->handle, buf, LINE_END_WIDTH);
+       dfm_put_record (t->writer, buf, LINE_END_WIDTH);
     }
 
   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, nullstr);
-  dump (0, "#include \"settings.h\"");
-  dump (0, "#include \"subclist.h\"");
-  dump (0, nullstr);
 }
 
 /* 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 \"settings.h\"");
          dump (0, "#include \"str.h\"");
+          dump (0, "#include \"subclist.h\"");
          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 "dictionary.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 "dictionary.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 "dictionary.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 "dictionary.h"
 #include "error.h"
 #include "expr.h"
 #include "lexer.h"
index 8ac6f8bddc2061d0b60a9f5330587f75ce8fa8ae..9c0cc6d3115b1b37cc411471856e3f0ac35be844 100644 (file)
    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 <setjmp.h>
 #include "alloc.h"
 #include "case.h"
+#include "dictionary.h"
 #include "error.h"
 #include "file-handle.h"
 #include "filename.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 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 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;
 
-    /* Uncompression buffer. */
+    /* Decompression buffer. */
     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. */
   };
 
-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. */
 
-/* bswap_int32(): Reverse the byte order of 32-bit integer *X. */
+/* Swap bytes *A and *B. */
 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
-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
@@ -152,172 +145,152 @@ corrupt_msg (int class, const char *format,...)
 }
 
 /* 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
-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. */
 
-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. */
-#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."),
-           handle_get_filename (h), strerror (errno));
+           handle_get_filename (r->fh), strerror (errno));
       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. */
-  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."),
-              handle_get_filename (h)));
+              handle_get_filename (fh)));
 
-      dict_set_weight (ext->dict, wv);
+      dict_set_weight (*dict, weight_var);
     }
   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;
 
-      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:
-         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."),
-                handle_get_filename (h)));
+                handle_get_filename (r->fh)));
 
        case 6:
-         if (!read_documents (h))
-           goto lossage;
+         if (!read_documents (r, *dict))
+           goto error;
          break;
 
        case 7:
@@ -332,8 +305,8 @@ sfm_read_dictionary (struct file_handle *h, struct sfm_read_info *inf)
 
            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);
@@ -343,13 +316,13 @@ sfm_read_dictionary (struct file_handle *h, struct sfm_read_info *inf)
            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:
-               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:
@@ -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."),
-                     handle_get_filename (h), data.subtype);
+                     handle_get_filename (r->fh), data.subtype);
                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)
-                 goto lossage;
+                 goto error;
                free (x);
              }
          }
@@ -379,43 +352,37 @@ sfm_read_dictionary (struct file_handle *h, struct sfm_read_info *inf)
          {
            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."),
-                 handle_get_filename (h), rec_type));
+                 handle_get_filename (r->fh), rec_type));
        }
     }
 
-break_out_of_loop:
+success:
   /* 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. */
-  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
-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;
 
@@ -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."),
-          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]);
 
-  /* 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."),
-           handle_get_filename (h)));
+           handle_get_filename (r->fh)));
+#else
+#error Add support for your floating-point format.
 #endif
 
-  /* PORTME: Check recorded file endianness against intuited file
-     endianness. */
 #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))
-    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. */
-  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;
 
-lossage:
+error:
   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];
-
   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)
     {
-      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."),
-          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;
 
-lossage:
+error:
   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 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;
 
-  /* Create the dictionary. */
-  dict = ext->dict = dict_create ();
-
   /* 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."),
-          handle_get_filename (h)));
+          handle_get_filename (r->fh)));
 
   /* 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. */
-  /* PORTME: endianness. */
   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."),
-              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.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. */
-  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."),
-           handle_get_filename (h), hdr.case_size,
+           handle_get_filename (r->fh), r->value_cnt,
            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)."),
-          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."),
-                 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. */
@@ -629,68 +588,67 @@ read_header (struct file_handle *h, struct sfm_read_info *inf)
        }
   }
 
-  if (inf)
+  if (info)
     {
       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
-      inf->bigendian = !ext->reverse_endian;
+      info->big_endian = !r->reverse_endian;
 #else
-      inf->bigendian = ext->reverse_endian;
+      info->big_endian = r->reverse_endian;
 #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;
-      strcpy (inf->product, cp);
+      strcpy (info->product, cp);
     }
 
   return 1;
 
-lossage:
+error:
   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
-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;
 
-  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. */
+  size_t var_cap = 0;
 
   /* 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. */
-  for (i = 0; i < ext->case_size; i++)
+  for (i = 0; i < r->value_cnt; i++)
     {
       struct variable *vv;
       char name[9];
+      int nv;
       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);
@@ -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."),
-               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
@@ -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."),
-                   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."),
-               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."),
-              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 "
-              "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 "
-                    "-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."),
-               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."),
-             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."),
-             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. */
@@ -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."),
-                   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 == '@'
@@ -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."),
-                   handle_get_filename (h), i, c, c));
+                   handle_get_filename (r->fh), i, c, c));
        }
       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."),
-               handle_get_filename (h), name));
+               handle_get_filename (r->fh), name));
 
       /* 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)
@@ -799,20 +753,20 @@ read_variables (struct file_handle *h, struct variable ***var_by_index)
          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."),
-                   handle_get_filename (h), vv->name, len));
+                   handle_get_filename (r->fh), vv->name, len));
 
          /* 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)
-           goto lossage;
+           goto error;
          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."),
-                   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]);
 
@@ -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."),
-                       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];
                }
-             else if (mv[1] == ext->highest)
+             else if (mv[1] == r->highest)
                {
                  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;
 
-      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."),
-           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."),
-           handle_get_filename (h), ext->case_size, next_value));
+           handle_get_filename (r->fh), r->value_cnt, next_value));
 
   return 1;
 
-lossage:
-  dict_destroy (dict);
-  ext->dict = NULL;
-
+error:
   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)."),
-          handle_get_filename (h), (s >> 16) & 0xff));
+          handle_get_filename (r->fh), (s >> 16) & 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)."),
-          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."),
-          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;
 
-lossage:
+error:
   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. */
@@ -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. */
-  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. */
@@ -976,15 +936,15 @@ read_value_labels (struct file_handle *h, struct variable **var_by_index)
       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. */
-      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);
-      assertive_bufread (h, label->label, padded_len - 1, 0);
+      assertive_buf_read (r, label->label, padded_len - 1, 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;
     
-    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."),
-             handle_get_filename (h)));
+             handle_get_filename (r->fh)));
   }
 
   /* 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);
-  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)."),
-          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++)
     {
-      int32 var_index;
+      int32 var_idx;
       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)."),
-              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. */
-      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."),
-               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)."),
-               handle_get_filename (h), v->name));
+               handle_get_filename (r->fh), v->name));
 
       /* 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."),
-             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")));
 
@@ -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);
-          if (ext->reverse_endian)
+          if (r->reverse_endian)
             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."),
-                 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."),
-                 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;
 
-lossage:
+error:
   if (labels) 
     {
       for (i = 0; i < n_labels; i++)
@@ -1118,62 +1078,58 @@ lossage:
   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
-   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 *
-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)
-    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."),
-             handle_get_filename (h), strerror (errno));
+             handle_get_filename (r->fh), strerror (errno));
       else
        corrupt_msg (ME, _("%s: Unexpected end of file."),
-                     handle_get_filename (h));
+                     handle_get_filename (r->fh));
       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
-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."),
-          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."),
-          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;
-  documents[80 * n_lines] = '\0';
+  documents[80 * line_cnt] = '\0';
   dict_set_documents (dict, documents);
   free (documents);
   return 1;
 
-lossage:
+error:
   return 0;
 }
 \f
@@ -1183,43 +1139,40 @@ lossage:
    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;
 
-  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."),
-           handle_get_filename (h), strerror (errno));
+           handle_get_filename (r->fh), strerror (errno));
       return 0;
     }
-  ext->ptr = ext->buf;
-  ext->end = &ext->buf[amt];
+  r->ptr = r->buf;
+  r->end = &r->buf[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
-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 (;;)
     {
@@ -1231,152 +1184,181 @@ read_compressed_data (struct file_handle *h, flt64 *temp)
            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."),
-                     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. */
-           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."),
-                         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. */
-           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. */
-           *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). */
-           *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. */
-      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."),
-                     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);
 
-winnage:
+success:
   /* We have filled up an entire record.  Update state and return
      successfully. */
-  ext->y = ++p;
+  r->y = ++p;
   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;
 }
 
-/* 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
-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
-       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>
-#include "sfm.h"
+#include "sfm-write.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 "case.h"
+#include "dictionary.h"
 #include "error.h"
 #include "file-handle.h"
 #include "getline.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
 
-/* 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. */
-    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 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 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."),
-           handle_get_filename (inf->h), strerror (errno));
+           handle_get_filename (w->fh), strerror (errno));
       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. */
-  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_variable (inf, dict_get_var (d, i));
+    write_variable (w, dict_get_var (d, i));
 
   /* 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);
 
-      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. */
   {
@@ -158,22 +182,41 @@ sfm_write_dictionary (struct sfm_write_info *inf)
     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. */
@@ -187,13 +230,10 @@ rerange (int 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
-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;
@@ -210,31 +250,17 @@ write_header (struct sfm_write_info *inf)
 
   hdr.layout_code = 2;
 
-  hdr.case_size = 0;
+  w->flt64_cnt = 0;
   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;
-      int recalc_weight_index = 1;
+      int recalc_weight_idx = 1;
       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;
-         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
-    hdr.weight_index = 0;
+    hdr.weight_idx = 0;
 
-  hdr.ncases = -1;
+  hdr.case_cnt = -1;
   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);
@@ -262,10 +287,10 @@ write_header (struct sfm_write_info *inf)
   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);
@@ -291,7 +316,7 @@ write_header (struct sfm_write_info *inf)
   
   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;
 }
@@ -305,18 +330,18 @@ write_format_spec (struct fmt_spec *src, int32 *dest)
 }
 
 /* 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
-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. */
-  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.type = (v->type == NUMERIC ? 0 : v->width);
+  sv.type = v->width;
   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));
-  if (!bufwrite (inf->h, &sv, sizeof sv))
+  if (!buf_write (w, &sv, sizeof sv))
     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);
 
-      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))
@@ -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++)
-       if (!bufwrite (inf->h, &sv, sizeof sv))
+       if (!buf_write (w, &sv, sizeof sv))
          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
-   index INDEX to the system file associated with INF.  Returns
+   index IDX to system file W.  Returns
    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
     {
@@ -433,7 +458,7 @@ write_value_labels (struct sfm_write_info * inf, struct variable *v, int index)
       flt64 labels[1] P;
     };
 
-  struct variable_index_rec
+  struct var_idx_rec
     {
       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 variable_index_rec vir;
+  struct var_idx_rec vir;
   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));
     }
   
-  if (!bufwrite (inf->h, vlr, vlr_size))
+  if (!buf_write (w, vlr, vlr_size))
     {
       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.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;
@@ -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
-write_documents (struct sfm_write_info * inf)
+write_documents (struct sfm_writer *w, const struct dictionary *d)
 {
-  struct dictionary *d = inf->dict;
   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;
@@ -511,9 +535,9 @@ write_documents (struct sfm_write_info * inf)
 
   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;
-  if (!bufwrite (inf->h, documents, 80 * n_lines))
+  if (!buf_write (w, documents, 80 * n_lines))
     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
-write_rec_7_34 (struct sfm_write_info * inf)
+write_rec_7_34 (struct sfm_writer *w)
 {
   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;
 
-  if (!bufwrite (inf->h, &rec_7, sizeof rec_7))
+  if (!buf_write (w, &rec_7, sizeof rec_7))
     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
-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."),
-           handle_get_filename (h), strerror (errno));
+           handle_get_filename (w->fh), strerror (errno));
       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
-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;
 }
 
-/* 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
-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. */
-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
   {
-    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. */
index 6eba150b9928032db393430dbcd0c9083a4e0fda..733aa9fbd31c89f5548ebe4ea5c7b4b4a9649d01 100644 (file)
@@ -21,6 +21,7 @@
 #include <stdlib.h>
 #include "alloc.h"
 #include "command.h"
+#include "dictionary.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;
 }
 
+/* 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. */
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
 
-#if !HAVE_STPCPY && !__linux__
+#if !HAVE_STPCPY
   char *stpcpy (char *dest, const char *src);
 #endif
 
index 5a91fc39b4e8d05b3bb9f3780dc5e84e11f014dd..5c8f6a651c2759c82f7965d1c114e117dfb918f0 100644 (file)
 #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 "sfm.h"
+#include "sfm-read.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 sfm_read_info inf;
+  struct sfm_reader *reader;
+  struct sfm_read_info info;
   int r, nr;
   int i;
 
   lex_match_id ("FILE");
   lex_match ('=');
 
-  h = fh_parse_file_handle ();
+  h = fh_parse ();
   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;
+  sfm_close_reader (reader);
 
   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",
-               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, 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,
-               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:"));
@@ -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,
-               _("Compression %s."), inf.compressed ? _("on") : _("off"));
+               _("Compression %s."), info.compressed ? _("on") : _("off"));
   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 "dictionary.h"
 #include "lexer.h"
 #include "error.h"
 #include "magic.h"
@@ -42,6 +43,7 @@
 #include "group_proc.h"
 #include "casefile.h"
 #include "levene.h"
+/* (headers) */
 
 /* (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 ) 
        {
-         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);
        }
     }
@@ -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];
-      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);
@@ -849,7 +851,7 @@ ssbox_paired_populate(struct ssbox *ssb,struct cmd_t_test *cmd UNUSED)
        {
          struct group_statistics *gs;
 
-         gs=&pairs[i].v[j]->p.grp_data.ugs;
+         gs = &group_proc_get (pairs[i].v[j])->ugs;
 
          /* 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)
     {
-      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);
@@ -1011,8 +1012,9 @@ trbox_independent_samples_populate(struct trbox *self,
       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 ;
@@ -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_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 */
-      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 );
 
@@ -1282,8 +1283,7 @@ trbox_one_sample_populate(struct trbox *trb, struct cmd_t_test *cmd)
       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);
@@ -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);
 
-      gs= &cmd->v_variables[i]->p.grp_data.ugs;
+      gs= &group_proc_get (cmd->v_variables[i])->ugs;
 
       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;
-      gs= &cmd->v_variables[i]->p.grp_data.ugs;
+      gs= &group_proc_get (cmd->v_variables[i])->ugs;
       
       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;
-      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(
@@ -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);
 
-      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]);
@@ -1551,7 +1551,7 @@ one_sample_precalc ( struct cmd_t_test *cmd )
   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;
     }
@@ -1566,7 +1566,7 @@ one_sample_postcalc (struct cmd_t_test *cmd)
   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 ;
     }
@@ -1715,7 +1715,7 @@ group_precalc (struct cmd_t_test *cmd )
 
   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;
@@ -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 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);
@@ -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];
-      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;
index c04ce764d99b18a99274fe2adfa9503f9f5854b7..0edb37f5ff772a3a7f48ff8b5147755e6b1933f8 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include "alloc.h"
 #include "command.h"
+#include "dictionary.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 "dictionary.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 <string.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;
index d7756fce2830ffb134963f9ef51e4cf91f5fcd15..46a0efc5fc0a061d83f315ec9f6cacb93741efa9 100644 (file)
--- a/src/var.h
+++ b/src/var.h
@@ -22,7 +22,6 @@
 
 #include <stddef.h>
 #include "format.h"
-#include "group_proc.h"
 #include "val.h"
 
 /* Frequency tables. */
@@ -67,93 +66,6 @@ struct freq_tab
     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. */
@@ -164,8 +76,7 @@ enum
   };
 
 /* 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 (?). */
@@ -208,25 +119,20 @@ struct variable
     struct val_labs *val_labs;  /* Value labels. */
     char *label;               /* Variable label. */
 
-    /* Per-procedure info. */
+    /* Per-command info. */
     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 *);
 
+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 
   {
@@ -247,82 +153,6 @@ struct vector
     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);
 
@@ -360,6 +190,7 @@ void cancel_temporary (void);
 \f
 /* Functions. */
 
+struct ccase;
 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 "dictionary.h"
 #include "do-ifP.h"
 #include "expr.h"
 #include "file-handle.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
@@ -67,7 +106,7 @@ void
 discard_variables (void)
 {
   dict_clear (default_dict);
-  default_handle = inline_file;
+  default_handle = NULL;
 
   n_lag = 0;
   
index 91376a5a68eccd08c74270fa0d1e10b189a65679..3a83964facbdcb378e6246a1366e3ab82a1b9a98 100644 (file)
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include "alloc.h"
 #include "bitvector.h"
+#include "dictionary.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 "dictionary.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 "dictionary.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 "dictionary.h"
 #include "error.h"
 #include "lexer.h"
 #include "str.h"