Patch #5209
authorJohn Darrington <john@darrington.wattle.id.au>
Mon, 17 Jul 2006 10:45:42 +0000 (10:45 +0000)
committerJohn Darrington <john@darrington.wattle.id.au>
Mon, 17 Jul 2006 10:45:42 +0000 (10:45 +0000)
Made casefile.c into an abstract base class, and moved the implementation to
fastfile.c.
Added a second implementation: flexifile.c which supports random access.
Currently, it's a very naive implementation.
Updated the gui to use flexifile.

32 files changed:
po/de.po
po/pspp.pot
src/automake.mk
src/data/ChangeLog
src/data/automake.mk
src/data/casefile-private.h [new file with mode: 0644]
src/data/casefile.c
src/data/casefile.h
src/data/fastfile.c [new file with mode: 0644]
src/data/fastfile.h [new file with mode: 0644]
src/data/procedure.c
src/data/scratch-writer.c
src/data/storage-stream.c
src/language/data-io/get.c
src/language/tests/casefile-test.c
src/math/sort.c
src/ui/ChangeLog
src/ui/automake.mk [new file with mode: 0644]
src/ui/flexifile.c [new file with mode: 0644]
src/ui/flexifile.h [new file with mode: 0644]
src/ui/gui/ChangeLog
src/ui/gui/automake.mk
src/ui/gui/menu-actions.c
src/ui/gui/psppire-case-file.c
src/ui/gui/psppire-case-file.h
src/ui/gui/psppire-data-store.c
src/ui/gui/psppire-dict.c
src/ui/gui/psppire-dict.h
src/ui/gui/psppire-variable.c
src/ui/gui/psppire.c
src/ui/gui/psppire.glade
src/ui/terminal/automake.mk

index c105fe7100c96881d3a498947e99d1007b493684..47c0180e2b8e6aaecfc01e7e55322db868783c93 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -10,7 +10,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PSPP 0.4.2\n"
 "Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
 msgstr ""
 "Project-Id-Version: PSPP 0.4.2\n"
 "Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
-"POT-Creation-Date: 2006-07-15 12:13+0800\n"
+"POT-Creation-Date: 2006-07-15 18:15+0800\n"
 "PO-Revision-Date: 2006-05-26 17:49+0800\n"
 "Last-Translator: John Darrington <john@darrington.wattle.id.au>\n"
 "Language-Team: German <pspp-dev@gnu.org>\n"
 "PO-Revision-Date: 2006-05-26 17:49+0800\n"
 "Last-Translator: John Darrington <john@darrington.wattle.id.au>\n"
 "Language-Team: German <pspp-dev@gnu.org>\n"
@@ -47,36 +47,6 @@ msgstr "Tag %d muß zwischen 0 bit 31 sein."
 msgid "Date %04d-%d-%d is before the earliest acceptable date of 1582-10-15."
 msgstr ""
 
 msgid "Date %04d-%d-%d is before the earliest acceptable date of 1582-10-15."
 msgstr ""
 
-#: src/data/casefile.c:269
-#, c-format
-msgid "%s: Removing temporary file: %s."
-msgstr ""
-
-#: src/data/casefile.c:437
-#, c-format
-msgid "Error writing temporary file: %s."
-msgstr ""
-
-#: src/data/casefile.c:600
-#, c-format
-msgid "%s: Opening temporary file: %s."
-msgstr ""
-
-#: src/data/casefile.c:643
-#, c-format
-msgid "%s: Seeking temporary file: %s."
-msgstr ""
-
-#: src/data/casefile.c:662
-#, c-format
-msgid "%s: Reading temporary file: %s."
-msgstr ""
-
-#: src/data/casefile.c:665
-#, c-format
-msgid "%s: Temporary file ended unexpectedly."
-msgstr ""
-
 #: src/data/data-in.c:59
 #, c-format
 msgid "(column %d"
 #: src/data/data-in.c:59
 #, c-format
 msgid "(column %d"
@@ -298,6 +268,36 @@ msgid ""
 "system-missing, zero, or negative.  These case(s) were ignored."
 msgstr ""
 
 "system-missing, zero, or negative.  These case(s) were ignored."
 msgstr ""
 
+#: src/data/fastfile.c:499
+#, c-format
+msgid "%s: Removing temporary file: %s."
+msgstr ""
+
+#: src/data/fastfile.c:623
+#, c-format
+msgid "Error writing temporary file: %s."
+msgstr ""
+
+#: src/data/fastfile.c:651
+#, c-format
+msgid "%s: Opening temporary file: %s."
+msgstr ""
+
+#: src/data/fastfile.c:695
+#, c-format
+msgid "%s: Seeking temporary file: %s."
+msgstr ""
+
+#: src/data/fastfile.c:717
+#, c-format
+msgid "%s: Reading temporary file: %s."
+msgstr ""
+
+#: src/data/fastfile.c:720
+#, c-format
+msgid "%s: Temporary file ended unexpectedly."
+msgstr ""
+
 #: src/data/file-handle-def.c:304
 #, c-format
 msgid "Can't open %s as a %s because it is already open as a %s."
 #: src/data/file-handle-def.c:304
 #, c-format
 msgid "Can't open %s as a %s because it is already open as a %s."
@@ -390,13 +390,13 @@ msgstr ""
 
 #: src/data/format.c:198 src/data/por-file-reader.c:481
 #: src/data/sys-file-reader.c:1220 src/data/sys-file-reader.c:1229
 
 #: src/data/format.c:198 src/data/por-file-reader.c:481
 #: src/data/sys-file-reader.c:1220 src/data/sys-file-reader.c:1229
-#: src/ui/gui/psppire.glade:1162 src/ui/gui/psppire-var-store.c:451
+#: src/ui/gui/psppire.glade:1192 src/ui/gui/psppire-var-store.c:451
 msgid "String"
 msgstr "Zeichenkette"
 
 #: src/data/format.c:198 src/data/por-file-reader.c:481
 #: src/data/sys-file-reader.c:1220 src/data/sys-file-reader.c:1229
 msgid "String"
 msgstr "Zeichenkette"
 
 #: src/data/format.c:198 src/data/por-file-reader.c:481
 #: src/data/sys-file-reader.c:1220 src/data/sys-file-reader.c:1229
-#: src/ui/gui/psppire.glade:1023 src/ui/gui/psppire-var-store.c:444
+#: src/ui/gui/psppire.glade:1053 src/ui/gui/psppire-var-store.c:444
 msgid "Numeric"
 msgstr "Nummer"
 
 msgid "Numeric"
 msgstr "Nummer"
 
@@ -1357,21 +1357,21 @@ msgstr ""
 msgid "Handle for %s not allowed here."
 msgstr ""
 
 msgid "Handle for %s not allowed here."
 msgstr ""
 
-#: src/language/data-io/get.c:115
+#: src/language/data-io/get.c:116
 msgid "expecting COMM or TAPE"
 msgstr ""
 
 msgid "expecting COMM or TAPE"
 msgstr ""
 
-#: src/language/data-io/get.c:357 src/language/data-io/get.c:371
-#: src/language/data-io/get.c:396
+#: src/language/data-io/get.c:358 src/language/data-io/get.c:372
+#: src/language/data-io/get.c:397
 #, c-format
 msgid "expecting %s or %s"
 msgstr ""
 
 #, c-format
 msgid "expecting %s or %s"
 msgstr ""
 
-#: src/language/data-io/get.c:605 src/language/data-io/print.c:186
+#: src/language/data-io/get.c:606 src/language/data-io/print.c:186
 msgid "expecting a valid subcommand"
 msgstr ""
 
 msgid "expecting a valid subcommand"
 msgstr ""
 
-#: src/language/data-io/get.c:638
+#: src/language/data-io/get.c:639
 #, c-format
 msgid ""
 "Cannot rename %s as %s because there already exists a variable named %s.  To "
 #, c-format
 msgid ""
 "Cannot rename %s as %s because there already exists a variable named %s.  To "
@@ -1379,75 +1379,75 @@ msgid ""
 "as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, \"/RENAME (A B C=B C A)\"."
 msgstr ""
 
 "as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, \"/RENAME (A B C=B C A)\"."
 msgstr ""
 
-#: src/language/data-io/get.c:663
+#: src/language/data-io/get.c:664
 msgid "`=' expected after variable list."
 msgstr ""
 
 msgid "`=' expected after variable list."
 msgstr ""
 
-#: src/language/data-io/get.c:670
+#: src/language/data-io/get.c:671
 #, c-format
 msgid ""
 "Number of variables on left side of `=' (%d) does not match number of "
 "variables on right side (%d), in parenthesized group %d of RENAME subcommand."
 msgstr ""
 
 #, c-format
 msgid ""
 "Number of variables on left side of `=' (%d) does not match number of "
 "variables on right side (%d), in parenthesized group %d of RENAME subcommand."
 msgstr ""
 
-#: src/language/data-io/get.c:683
+#: src/language/data-io/get.c:684
 #, c-format
 msgid "Requested renaming duplicates variable name %s."
 msgstr ""
 
 #, c-format
 msgid "Requested renaming duplicates variable name %s."
 msgstr ""
 
-#: src/language/data-io/get.c:713
+#: src/language/data-io/get.c:714
 msgid "Cannot DROP all variables from dictionary."
 msgstr ""
 
 msgid "Cannot DROP all variables from dictionary."
 msgstr ""
 
-#: src/language/data-io/get.c:890
+#: src/language/data-io/get.c:891
 msgid "The active file may not be specified more than once."
 msgstr ""
 
 msgid "The active file may not be specified more than once."
 msgstr ""
 
-#: src/language/data-io/get.c:898
+#: src/language/data-io/get.c:899
 msgid "Cannot specify the active file since no active file has been defined."
 msgstr ""
 
 msgid "Cannot specify the active file since no active file has been defined."
 msgstr ""
 
-#: src/language/data-io/get.c:905
+#: src/language/data-io/get.c:906
 msgid ""
 "MATCH FILES may not be used after TEMPORARY when the active file is an input "
 "source.  Temporary transformations will be made permanent."
 msgstr ""
 
 msgid ""
 "MATCH FILES may not be used after TEMPORARY when the active file is an input "
 "source.  Temporary transformations will be made permanent."
 msgstr ""
 
-#: src/language/data-io/get.c:941
+#: src/language/data-io/get.c:942
 msgid "Multiple IN subcommands for a single FILE or TABLE."
 msgstr ""
 
 msgid "Multiple IN subcommands for a single FILE or TABLE."
 msgstr ""
 
-#: src/language/data-io/get.c:961
+#: src/language/data-io/get.c:962
 msgid "BY may appear at most once."
 msgstr ""
 
 msgid "BY may appear at most once."
 msgstr ""
 
-#: src/language/data-io/get.c:981
+#: src/language/data-io/get.c:982
 #, c-format
 msgid "File %s lacks BY variable %s."
 msgstr ""
 
 #, c-format
 msgid "File %s lacks BY variable %s."
 msgstr ""
 
-#: src/language/data-io/get.c:995
+#: src/language/data-io/get.c:996
 msgid "FIRST may appear at most once."
 msgstr ""
 
 msgid "FIRST may appear at most once."
 msgstr ""
 
-#: src/language/data-io/get.c:1009
+#: src/language/data-io/get.c:1010
 msgid "LAST may appear at most once."
 msgstr ""
 
 msgid "LAST may appear at most once."
 msgstr ""
 
-#: src/language/data-io/get.c:1050
+#: src/language/data-io/get.c:1051
 msgid "BY is required when TABLE is specified."
 msgstr ""
 
 msgid "BY is required when TABLE is specified."
 msgstr ""
 
-#: src/language/data-io/get.c:1055
+#: src/language/data-io/get.c:1056
 msgid "BY is required when IN is specified."
 msgstr ""
 
 msgid "BY is required when IN is specified."
 msgstr ""
 
-#: src/language/data-io/get.c:1083
+#: src/language/data-io/get.c:1084
 #, c-format
 msgid "IN variable name %s duplicates an existing variable name."
 msgstr ""
 
 #, c-format
 msgid "IN variable name %s duplicates an existing variable name."
 msgstr ""
 
-#: src/language/data-io/get.c:1537
+#: src/language/data-io/get.c:1538
 #, c-format
 msgid ""
 "Variable %s in file %s (%s) has different type or width from the same "
 #, c-format
 msgid ""
 "Variable %s in file %s (%s) has different type or width from the same "
@@ -3441,7 +3441,7 @@ msgstr ""
 msgid "Coefficient Correlations"
 msgstr ""
 
 msgid "Coefficient Correlations"
 msgstr ""
 
-#: src/language/stats/regression.q:1122
+#: src/language/stats/regression.q:1129
 msgid "Dependent variable must be numeric."
 msgstr ""
 
 msgid "Dependent variable must be numeric."
 msgstr ""
 
@@ -3855,7 +3855,7 @@ msgstr ""
 msgid "Empirical with averaging"
 msgstr ""
 
 msgid "Empirical with averaging"
 msgstr ""
 
-#: src/math/sort.c:439
+#: src/math/sort.c:440
 #, c-format
 msgid ""
 "Out of memory.  Could not allocate room for minimum of %d cases of %d bytes "
 #, c-format
 msgid ""
 "Out of memory.  Could not allocate room for minimum of %d cases of %d bytes "
@@ -4272,27 +4272,27 @@ msgstr "Unbetitelt"
 msgid "PSPP Data Editor"
 msgstr "PSPP Dateiaufbereiter"
 
 msgid "PSPP Data Editor"
 msgstr "PSPP Dateiaufbereiter"
 
-#: src/ui/gui/menu-actions.c:222 src/ui/gui/psppire.glade:430
+#: src/ui/gui/menu-actions.c:220 src/ui/gui/psppire.glade:459
 msgid "Open"
 msgstr "Öffen"
 
 msgid "Open"
 msgstr "Öffen"
 
-#: src/ui/gui/menu-actions.c:230
+#: src/ui/gui/menu-actions.c:228
 msgid "System Files (*.sav)"
 msgstr "Systemedatein (*.sav)"
 
 msgid "System Files (*.sav)"
 msgstr "Systemedatein (*.sav)"
 
-#: src/ui/gui/menu-actions.c:236
+#: src/ui/gui/menu-actions.c:234
 msgid "Portable Files (*.por) "
 msgstr "Tragbardatein (*.por)"
 
 msgid "Portable Files (*.por) "
 msgstr "Tragbardatein (*.por)"
 
-#: src/ui/gui/menu-actions.c:242
+#: src/ui/gui/menu-actions.c:240
 msgid "All Files"
 msgstr "Alle Datei"
 
 msgid "All Files"
 msgstr "Alle Datei"
 
-#: src/ui/gui/menu-actions.c:274
+#: src/ui/gui/menu-actions.c:272
 msgid "Save Data As"
 msgstr "Speichern unter"
 
 msgid "Save Data As"
 msgstr "Speichern unter"
 
-#: src/ui/gui/menu-actions.c:566
+#: src/ui/gui/menu-actions.c:568
 msgid "Font Selection"
 msgstr "Schriftwahlung"
 
 msgid "Font Selection"
 msgstr "Schriftwahlung"
 
@@ -4320,11 +4320,11 @@ msgstr "Falshe Spannweitebeschreibung"
 msgid "Sorry. The help system hasn't yet been implemented."
 msgstr "Es gibt noch nicht kein Helpsysteme. Schade!"
 
 msgid "Sorry. The help system hasn't yet been implemented."
 msgstr "Es gibt noch nicht kein Helpsysteme. Schade!"
 
-#: src/ui/gui/psppire-data-store.c:693
+#: src/ui/gui/psppire-data-store.c:695
 msgid "var"
 msgstr ""
 
 msgid "var"
 msgstr ""
 
-#: src/ui/gui/psppire-data-store.c:777 src/ui/gui/psppire-var-store.c:518
+#: src/ui/gui/psppire-data-store.c:779 src/ui/gui/psppire-var-store.c:518
 #: src/ui/gui/psppire-var-store.c:528 src/ui/gui/psppire-var-store.c:538
 #: src/ui/gui/psppire-var-store.c:735
 #, c-format
 #: src/ui/gui/psppire-var-store.c:528 src/ui/gui/psppire-var-store.c:538
 #: src/ui/gui/psppire-var-store.c:735
 #, c-format
@@ -4339,147 +4339,157 @@ msgstr "_Datei"
 msgid "_Edit"
 msgstr "_Bearbeiten"
 
 msgid "_Edit"
 msgstr "_Bearbeiten"
 
+#: src/ui/gui/psppire.glade:140
+#, fuzzy
+msgid "Paste _Variables"
+msgstr "Variableansicht"
+
 #: src/ui/gui/psppire.glade:148
 #: src/ui/gui/psppire.glade:148
-msgid "_Insert"
-msgstr "_Stecken"
+msgid "Cl_ear"
+msgstr ""
 
 
-#: src/ui/gui/psppire.glade:161
+#: src/ui/gui/psppire.glade:165
+#, fuzzy
+msgid "_Find"
+msgstr "_Datei"
+
+#: src/ui/gui/psppire.glade:189
 msgid "_View"
 msgstr "_Ansicht"
 
 msgid "_View"
 msgstr "_Ansicht"
 
-#: src/ui/gui/psppire.glade:170
+#: src/ui/gui/psppire.glade:198
 msgid "Status Bar"
 msgstr "Statusleiste"
 
 msgid "Status Bar"
 msgstr "Statusleiste"
 
-#: src/ui/gui/psppire.glade:179
+#: src/ui/gui/psppire.glade:207
 msgid "Toolbars"
 msgstr "Werkzeugregal"
 
 msgid "Toolbars"
 msgstr "Werkzeugregal"
 
-#: src/ui/gui/psppire.glade:193
+#: src/ui/gui/psppire.glade:221
 msgid "Fonts"
 msgstr "Schrift"
 
 msgid "Fonts"
 msgstr "Schrift"
 
-#: src/ui/gui/psppire.glade:202
+#: src/ui/gui/psppire.glade:230
 msgid "Grid Lines"
 msgstr "Glitten"
 
 msgid "Grid Lines"
 msgstr "Glitten"
 
-#: src/ui/gui/psppire.glade:212 src/ui/gui/psppire.glade:718
-#: src/ui/gui/psppire.glade:1597 src/ui/gui/psppire.glade:1874
+#: src/ui/gui/psppire.glade:240 src/ui/gui/psppire.glade:748
+#: src/ui/gui/psppire.glade:1627 src/ui/gui/psppire.glade:1904
 msgid "Value Labels"
 msgstr "Werten"
 
 msgid "Value Labels"
 msgstr "Werten"
 
-#: src/ui/gui/psppire.glade:228
+#: src/ui/gui/psppire.glade:256
 msgid "Data"
 msgstr "Daten"
 
 msgid "Data"
 msgstr "Daten"
 
-#: src/ui/gui/psppire.glade:237 src/ui/gui/psppire.glade:549
+#: src/ui/gui/psppire.glade:265 src/ui/gui/psppire.glade:578
 msgid "Variables"
 msgstr "Variableansicht"
 
 msgid "Variables"
 msgstr "Variableansicht"
 
-#: src/ui/gui/psppire.glade:250
+#: src/ui/gui/psppire.glade:278
 #, fuzzy
 msgid "_Data"
 msgstr "Daten"
 
 #, fuzzy
 msgid "_Data"
 msgstr "Daten"
 
-#: src/ui/gui/psppire.glade:260 src/ui/gui/psppire.glade:624
+#: src/ui/gui/psppire.glade:288 src/ui/gui/psppire.glade:654
 #, fuzzy
 msgid "Insert Variable"
 msgstr "Variableansicht"
 
 #, fuzzy
 msgid "Insert Variable"
 msgstr "Variableansicht"
 
-#: src/ui/gui/psppire.glade:269
+#: src/ui/gui/psppire.glade:297
 #, fuzzy
 msgid "Insert Cases"
 msgstr "_Stecken"
 
 #, fuzzy
 msgid "Insert Cases"
 msgstr "_Stecken"
 
-#: src/ui/gui/psppire.glade:277 src/ui/gui/psppire.glade:533
-#: src/ui/gui/psppire.glade:2439
+#: src/ui/gui/psppire.glade:306 src/ui/gui/psppire.glade:562
+#: src/ui/gui/psppire.glade:2469
 msgid "Go To Case"
 msgstr ""
 
 msgid "Go To Case"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:304 src/ui/gui/psppire.glade:2550
+#: src/ui/gui/psppire.glade:333 src/ui/gui/psppire.glade:2580
 msgid "Sort Cases"
 msgstr ""
 
 msgid "Sort Cases"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:314
+#: src/ui/gui/psppire.glade:343
 msgid "Transpose"
 msgstr ""
 
 msgid "Transpose"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:323
+#: src/ui/gui/psppire.glade:352
 msgid "Restructure"
 msgstr ""
 
 msgid "Restructure"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:332
+#: src/ui/gui/psppire.glade:361
 #, fuzzy
 msgid "Merge Files"
 msgstr "Alle Datei"
 
 #, fuzzy
 msgid "Merge Files"
 msgstr "Alle Datei"
 
-#: src/ui/gui/psppire.glade:341
+#: src/ui/gui/psppire.glade:370
 msgid "Aggregate"
 msgstr ""
 
 msgid "Aggregate"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:356 src/ui/gui/psppire.glade:654
+#: src/ui/gui/psppire.glade:385 src/ui/gui/psppire.glade:684
 #, fuzzy
 msgid "Split File"
 msgstr "Alle Datei"
 
 #, fuzzy
 msgid "Split File"
 msgstr "Alle Datei"
 
-#: src/ui/gui/psppire.glade:365 src/ui/gui/psppire.glade:688
+#: src/ui/gui/psppire.glade:394 src/ui/gui/psppire.glade:718
 msgid "Select Cases"
 msgstr ""
 
 msgid "Select Cases"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:374 src/ui/gui/psppire.glade:671
+#: src/ui/gui/psppire.glade:403 src/ui/gui/psppire.glade:701
 msgid "Weight Cases"
 msgstr ""
 
 msgid "Weight Cases"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:386
+#: src/ui/gui/psppire.glade:415
 msgid "_Help"
 msgstr "_Hilfe"
 
 msgid "_Help"
 msgstr "_Hilfe"
 
-#: src/ui/gui/psppire.glade:395
+#: src/ui/gui/psppire.glade:424
 msgid "_About"
 msgstr "_Info"
 
 msgid "_About"
 msgstr "_Info"
 
-#: src/ui/gui/psppire.glade:446
+#: src/ui/gui/psppire.glade:475
 msgid "Save"
 msgstr "Speichen"
 
 msgid "Save"
 msgstr "Speichen"
 
-#: src/ui/gui/psppire.glade:462
+#: src/ui/gui/psppire.glade:491
 msgid "Print"
 msgstr "Drucken"
 
 msgid "Print"
 msgstr "Drucken"
 
-#: src/ui/gui/psppire.glade:490
+#: src/ui/gui/psppire.glade:519
 msgid "Undo"
 msgstr ""
 
 msgid "Undo"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:505
+#: src/ui/gui/psppire.glade:534
 msgid "Redo"
 msgstr ""
 
 msgid "Redo"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:579
+#: src/ui/gui/psppire.glade:608
 msgid "Find"
 msgstr ""
 
 msgid "Find"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:607
+#: src/ui/gui/psppire.glade:636
 #, fuzzy
 msgid "Insert Case"
 msgstr "_Stecken"
 
 #, fuzzy
 msgid "Insert Case"
 msgstr "_Stecken"
 
-#: src/ui/gui/psppire.glade:738
+#: src/ui/gui/psppire.glade:768
 msgid "Use Sets"
 msgstr ""
 
 msgid "Use Sets"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:870
+#: src/ui/gui/psppire.glade:900
 msgid "Data View"
 msgstr "Datenansicht"
 
 msgid "Data View"
 msgstr "Datenansicht"
 
-#: src/ui/gui/psppire.glade:918
+#: src/ui/gui/psppire.glade:948
 msgid "Variable View"
 msgstr "Variableansicht"
 
 msgid "Variable View"
 msgstr "Variableansicht"
 
-#: src/ui/gui/psppire.glade:964
+#: src/ui/gui/psppire.glade:994
 msgid "This is pre-alpha software.  It probably will not work."
 msgstr "Diese Software ist vor-Alpha.  Wahrscheinlich Funktioniert es nicht."
 
 msgid "This is pre-alpha software.  It probably will not work."
 msgstr "Diese Software ist vor-Alpha.  Wahrscheinlich Funktioniert es nicht."
 
-#: src/ui/gui/psppire.glade:965
+#: src/ui/gui/psppire.glade:995
 msgid ""
 "    This program is free software; you can redistribute it and/or modify\n"
 "    it under the terms of the GNU General Public License as published by\n"
 msgid ""
 "    This program is free software; you can redistribute it and/or modify\n"
 "    it under the terms of the GNU General Public License as published by\n"
@@ -4497,107 +4507,107 @@ msgid ""
 "    02110-1301, USA.\n"
 msgstr ""
 
 "    02110-1301, USA.\n"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:990
+#: src/ui/gui/psppire.glade:1020
 msgid "Variable Type"
 msgstr "Variableansicht"
 
 msgid "Variable Type"
 msgstr "Variableansicht"
 
-#: src/ui/gui/psppire.glade:1042 src/ui/gui/psppire-var-store.c:445
+#: src/ui/gui/psppire.glade:1072 src/ui/gui/psppire-var-store.c:445
 msgid "Comma"
 msgstr "Komma"
 
 msgid "Comma"
 msgstr "Komma"
 
-#: src/ui/gui/psppire.glade:1062 src/ui/gui/psppire-var-store.c:446
+#: src/ui/gui/psppire.glade:1092 src/ui/gui/psppire-var-store.c:446
 msgid "Dot"
 msgstr "Punkt"
 
 msgid "Dot"
 msgstr "Punkt"
 
-#: src/ui/gui/psppire.glade:1082
+#: src/ui/gui/psppire.glade:1112
 msgid "Scientific notation"
 msgstr "Wissenschaftlichnotation"
 
 msgid "Scientific notation"
 msgstr "Wissenschaftlichnotation"
 
-#: src/ui/gui/psppire.glade:1102 src/ui/gui/psppire-var-store.c:448
+#: src/ui/gui/psppire.glade:1132 src/ui/gui/psppire-var-store.c:448
 msgid "Date"
 msgstr "Datum"
 
 msgid "Date"
 msgstr "Datum"
 
-#: src/ui/gui/psppire.glade:1122 src/ui/gui/psppire-var-store.c:449
+#: src/ui/gui/psppire.glade:1152 src/ui/gui/psppire-var-store.c:449
 msgid "Dollar"
 msgstr "Euro"
 
 msgid "Dollar"
 msgstr "Euro"
 
-#: src/ui/gui/psppire.glade:1142
+#: src/ui/gui/psppire.glade:1172
 msgid "Custom currency"
 msgstr "Spezialwährung"
 
 msgid "Custom currency"
 msgstr "Spezialwährung"
 
-#: src/ui/gui/psppire.glade:1287
+#: src/ui/gui/psppire.glade:1317
 msgid "positive"
 msgstr "positiv"
 
 msgid "positive"
 msgstr "positiv"
 
-#: src/ui/gui/psppire.glade:1312
+#: src/ui/gui/psppire.glade:1342
 msgid "negative"
 msgstr "negativ"
 
 msgid "negative"
 msgstr "negativ"
 
-#: src/ui/gui/psppire.glade:1341
+#: src/ui/gui/psppire.glade:1371
 msgid "Sample"
 msgstr "Muster"
 
 msgid "Sample"
 msgstr "Muster"
 
-#: src/ui/gui/psppire.glade:1419
+#: src/ui/gui/psppire.glade:1449
 msgid "Decimal Places:"
 msgstr "Dezimalstellen:"
 
 msgid "Decimal Places:"
 msgstr "Dezimalstellen:"
 
-#: src/ui/gui/psppire.glade:1496
+#: src/ui/gui/psppire.glade:1526
 msgid "Width:"
 msgstr "Große:"
 
 msgid "Width:"
 msgstr "Große:"
 
-#: src/ui/gui/psppire.glade:1715
+#: src/ui/gui/psppire.glade:1745
 msgid "Value:"
 msgstr "Werte:"
 
 msgid "Value:"
 msgstr "Werte:"
 
-#: src/ui/gui/psppire.glade:1743
+#: src/ui/gui/psppire.glade:1773
 msgid "Value Label:"
 msgstr "Kennsatz:"
 
 msgid "Value Label:"
 msgstr "Kennsatz:"
 
-#: src/ui/gui/psppire.glade:1959
+#: src/ui/gui/psppire.glade:1989
 msgid "Missing Values"
 msgstr "Lösewerten"
 
 msgid "Missing Values"
 msgstr "Lösewerten"
 
-#: src/ui/gui/psppire.glade:2045
+#: src/ui/gui/psppire.glade:2075
 msgid "_No missing values"
 msgstr "_Kein Lösewerten"
 
 msgid "_No missing values"
 msgstr "_Kein Lösewerten"
 
-#: src/ui/gui/psppire.glade:2070
+#: src/ui/gui/psppire.glade:2100
 msgid "_Discrete missing values"
 msgstr "_Diskret Lösewerten"
 
 msgid "_Discrete missing values"
 msgstr "_Diskret Lösewerten"
 
-#: src/ui/gui/psppire.glade:2199
+#: src/ui/gui/psppire.glade:2229
 msgid "_Range plus one optional discrete missing value"
 msgstr "Wertebereich und ein optional Lösewert"
 
 msgid "_Range plus one optional discrete missing value"
 msgstr "Wertebereich und ein optional Lösewert"
 
-#: src/ui/gui/psppire.glade:2236
+#: src/ui/gui/psppire.glade:2266
 msgid "_Low:"
 msgstr "_Tief:"
 
 msgid "_Low:"
 msgstr "_Tief:"
 
-#: src/ui/gui/psppire.glade:2295
+#: src/ui/gui/psppire.glade:2325
 msgid "_High:"
 msgstr "_Hoch:"
 
 msgid "_High:"
 msgstr "_Hoch:"
 
-#: src/ui/gui/psppire.glade:2369
+#: src/ui/gui/psppire.glade:2399
 msgid "Di_screte value:"
 msgstr "Di_skretwerte"
 
 msgid "Di_screte value:"
 msgstr "Di_skretwerte"
 
-#: src/ui/gui/psppire.glade:2495
+#: src/ui/gui/psppire.glade:2525
 msgid "Case Number:"
 msgstr ""
 
 msgid "Case Number:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2667 src/ui/gui/sort-cases-dialog.c:282
+#: src/ui/gui/psppire.glade:2697 src/ui/gui/sort-cases-dialog.c:282
 msgid "Ascending"
 msgstr ""
 
 msgid "Ascending"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2686 src/ui/gui/sort-cases-dialog.c:284
+#: src/ui/gui/psppire.glade:2716 src/ui/gui/sort-cases-dialog.c:284
 msgid "Descending"
 msgstr ""
 
 msgid "Descending"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2707
+#: src/ui/gui/psppire.glade:2737
 msgid "Sort Order"
 msgstr ""
 
 msgid "Sort Order"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2744
+#: src/ui/gui/psppire.glade:2774
 msgid "Sort by:"
 msgstr ""
 
 msgid "Sort by:"
 msgstr ""
 
@@ -4748,3 +4758,6 @@ msgstr "Fehler"
 #: src/ui/terminal/msg-ui.c:117
 msgid "warning"
 msgstr "Warnung"
 #: src/ui/terminal/msg-ui.c:117
 msgid "warning"
 msgstr "Warnung"
+
+#~ msgid "_Insert"
+#~ msgstr "_Stecken"
index 803e416ec5ec30a1c29444b8846b554205f0f89d..add94089e094af2230ea7bea6c4e3eacd05d89a9 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
-"POT-Creation-Date: 2006-07-15 12:13+0800\n"
+"POT-Creation-Date: 2006-07-15 18:15+0800\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -46,36 +46,6 @@ msgstr ""
 msgid "Date %04d-%d-%d is before the earliest acceptable date of 1582-10-15."
 msgstr ""
 
 msgid "Date %04d-%d-%d is before the earliest acceptable date of 1582-10-15."
 msgstr ""
 
-#: src/data/casefile.c:269
-#, c-format
-msgid "%s: Removing temporary file: %s."
-msgstr ""
-
-#: src/data/casefile.c:437
-#, c-format
-msgid "Error writing temporary file: %s."
-msgstr ""
-
-#: src/data/casefile.c:600
-#, c-format
-msgid "%s: Opening temporary file: %s."
-msgstr ""
-
-#: src/data/casefile.c:643
-#, c-format
-msgid "%s: Seeking temporary file: %s."
-msgstr ""
-
-#: src/data/casefile.c:662
-#, c-format
-msgid "%s: Reading temporary file: %s."
-msgstr ""
-
-#: src/data/casefile.c:665
-#, c-format
-msgid "%s: Temporary file ended unexpectedly."
-msgstr ""
-
 #: src/data/data-in.c:59
 #, c-format
 msgid "(column %d"
 #: src/data/data-in.c:59
 #, c-format
 msgid "(column %d"
@@ -297,6 +267,36 @@ msgid ""
 "system-missing, zero, or negative.  These case(s) were ignored."
 msgstr ""
 
 "system-missing, zero, or negative.  These case(s) were ignored."
 msgstr ""
 
+#: src/data/fastfile.c:499
+#, c-format
+msgid "%s: Removing temporary file: %s."
+msgstr ""
+
+#: src/data/fastfile.c:623
+#, c-format
+msgid "Error writing temporary file: %s."
+msgstr ""
+
+#: src/data/fastfile.c:651
+#, c-format
+msgid "%s: Opening temporary file: %s."
+msgstr ""
+
+#: src/data/fastfile.c:695
+#, c-format
+msgid "%s: Seeking temporary file: %s."
+msgstr ""
+
+#: src/data/fastfile.c:717
+#, c-format
+msgid "%s: Reading temporary file: %s."
+msgstr ""
+
+#: src/data/fastfile.c:720
+#, c-format
+msgid "%s: Temporary file ended unexpectedly."
+msgstr ""
+
 #: src/data/file-handle-def.c:304
 #, c-format
 msgid "Can't open %s as a %s because it is already open as a %s."
 #: src/data/file-handle-def.c:304
 #, c-format
 msgid "Can't open %s as a %s because it is already open as a %s."
@@ -389,13 +389,13 @@ msgstr ""
 
 #: src/data/format.c:198 src/data/por-file-reader.c:481
 #: src/data/sys-file-reader.c:1220 src/data/sys-file-reader.c:1229
 
 #: src/data/format.c:198 src/data/por-file-reader.c:481
 #: src/data/sys-file-reader.c:1220 src/data/sys-file-reader.c:1229
-#: src/ui/gui/psppire.glade:1162 src/ui/gui/psppire-var-store.c:451
+#: src/ui/gui/psppire.glade:1192 src/ui/gui/psppire-var-store.c:451
 msgid "String"
 msgstr ""
 
 #: src/data/format.c:198 src/data/por-file-reader.c:481
 #: src/data/sys-file-reader.c:1220 src/data/sys-file-reader.c:1229
 msgid "String"
 msgstr ""
 
 #: src/data/format.c:198 src/data/por-file-reader.c:481
 #: src/data/sys-file-reader.c:1220 src/data/sys-file-reader.c:1229
-#: src/ui/gui/psppire.glade:1023 src/ui/gui/psppire-var-store.c:444
+#: src/ui/gui/psppire.glade:1053 src/ui/gui/psppire-var-store.c:444
 msgid "Numeric"
 msgstr ""
 
 msgid "Numeric"
 msgstr ""
 
@@ -1356,21 +1356,21 @@ msgstr ""
 msgid "Handle for %s not allowed here."
 msgstr ""
 
 msgid "Handle for %s not allowed here."
 msgstr ""
 
-#: src/language/data-io/get.c:115
+#: src/language/data-io/get.c:116
 msgid "expecting COMM or TAPE"
 msgstr ""
 
 msgid "expecting COMM or TAPE"
 msgstr ""
 
-#: src/language/data-io/get.c:357 src/language/data-io/get.c:371
-#: src/language/data-io/get.c:396
+#: src/language/data-io/get.c:358 src/language/data-io/get.c:372
+#: src/language/data-io/get.c:397
 #, c-format
 msgid "expecting %s or %s"
 msgstr ""
 
 #, c-format
 msgid "expecting %s or %s"
 msgstr ""
 
-#: src/language/data-io/get.c:605 src/language/data-io/print.c:186
+#: src/language/data-io/get.c:606 src/language/data-io/print.c:186
 msgid "expecting a valid subcommand"
 msgstr ""
 
 msgid "expecting a valid subcommand"
 msgstr ""
 
-#: src/language/data-io/get.c:638
+#: src/language/data-io/get.c:639
 #, c-format
 msgid ""
 "Cannot rename %s as %s because there already exists a variable named %s.  To "
 #, c-format
 msgid ""
 "Cannot rename %s as %s because there already exists a variable named %s.  To "
@@ -1378,75 +1378,75 @@ msgid ""
 "as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, \"/RENAME (A B C=B C A)\"."
 msgstr ""
 
 "as \"/RENAME (A=B)(B=C)(C=A)\", or equivalently, \"/RENAME (A B C=B C A)\"."
 msgstr ""
 
-#: src/language/data-io/get.c:663
+#: src/language/data-io/get.c:664
 msgid "`=' expected after variable list."
 msgstr ""
 
 msgid "`=' expected after variable list."
 msgstr ""
 
-#: src/language/data-io/get.c:670
+#: src/language/data-io/get.c:671
 #, c-format
 msgid ""
 "Number of variables on left side of `=' (%d) does not match number of "
 "variables on right side (%d), in parenthesized group %d of RENAME subcommand."
 msgstr ""
 
 #, c-format
 msgid ""
 "Number of variables on left side of `=' (%d) does not match number of "
 "variables on right side (%d), in parenthesized group %d of RENAME subcommand."
 msgstr ""
 
-#: src/language/data-io/get.c:683
+#: src/language/data-io/get.c:684
 #, c-format
 msgid "Requested renaming duplicates variable name %s."
 msgstr ""
 
 #, c-format
 msgid "Requested renaming duplicates variable name %s."
 msgstr ""
 
-#: src/language/data-io/get.c:713
+#: src/language/data-io/get.c:714
 msgid "Cannot DROP all variables from dictionary."
 msgstr ""
 
 msgid "Cannot DROP all variables from dictionary."
 msgstr ""
 
-#: src/language/data-io/get.c:890
+#: src/language/data-io/get.c:891
 msgid "The active file may not be specified more than once."
 msgstr ""
 
 msgid "The active file may not be specified more than once."
 msgstr ""
 
-#: src/language/data-io/get.c:898
+#: src/language/data-io/get.c:899
 msgid "Cannot specify the active file since no active file has been defined."
 msgstr ""
 
 msgid "Cannot specify the active file since no active file has been defined."
 msgstr ""
 
-#: src/language/data-io/get.c:905
+#: src/language/data-io/get.c:906
 msgid ""
 "MATCH FILES may not be used after TEMPORARY when the active file is an input "
 "source.  Temporary transformations will be made permanent."
 msgstr ""
 
 msgid ""
 "MATCH FILES may not be used after TEMPORARY when the active file is an input "
 "source.  Temporary transformations will be made permanent."
 msgstr ""
 
-#: src/language/data-io/get.c:941
+#: src/language/data-io/get.c:942
 msgid "Multiple IN subcommands for a single FILE or TABLE."
 msgstr ""
 
 msgid "Multiple IN subcommands for a single FILE or TABLE."
 msgstr ""
 
-#: src/language/data-io/get.c:961
+#: src/language/data-io/get.c:962
 msgid "BY may appear at most once."
 msgstr ""
 
 msgid "BY may appear at most once."
 msgstr ""
 
-#: src/language/data-io/get.c:981
+#: src/language/data-io/get.c:982
 #, c-format
 msgid "File %s lacks BY variable %s."
 msgstr ""
 
 #, c-format
 msgid "File %s lacks BY variable %s."
 msgstr ""
 
-#: src/language/data-io/get.c:995
+#: src/language/data-io/get.c:996
 msgid "FIRST may appear at most once."
 msgstr ""
 
 msgid "FIRST may appear at most once."
 msgstr ""
 
-#: src/language/data-io/get.c:1009
+#: src/language/data-io/get.c:1010
 msgid "LAST may appear at most once."
 msgstr ""
 
 msgid "LAST may appear at most once."
 msgstr ""
 
-#: src/language/data-io/get.c:1050
+#: src/language/data-io/get.c:1051
 msgid "BY is required when TABLE is specified."
 msgstr ""
 
 msgid "BY is required when TABLE is specified."
 msgstr ""
 
-#: src/language/data-io/get.c:1055
+#: src/language/data-io/get.c:1056
 msgid "BY is required when IN is specified."
 msgstr ""
 
 msgid "BY is required when IN is specified."
 msgstr ""
 
-#: src/language/data-io/get.c:1083
+#: src/language/data-io/get.c:1084
 #, c-format
 msgid "IN variable name %s duplicates an existing variable name."
 msgstr ""
 
 #, c-format
 msgid "IN variable name %s duplicates an existing variable name."
 msgstr ""
 
-#: src/language/data-io/get.c:1537
+#: src/language/data-io/get.c:1538
 #, c-format
 msgid ""
 "Variable %s in file %s (%s) has different type or width from the same "
 #, c-format
 msgid ""
 "Variable %s in file %s (%s) has different type or width from the same "
@@ -3440,7 +3440,7 @@ msgstr ""
 msgid "Coefficient Correlations"
 msgstr ""
 
 msgid "Coefficient Correlations"
 msgstr ""
 
-#: src/language/stats/regression.q:1122
+#: src/language/stats/regression.q:1129
 msgid "Dependent variable must be numeric."
 msgstr ""
 
 msgid "Dependent variable must be numeric."
 msgstr ""
 
@@ -3854,7 +3854,7 @@ msgstr ""
 msgid "Empirical with averaging"
 msgstr ""
 
 msgid "Empirical with averaging"
 msgstr ""
 
-#: src/math/sort.c:439
+#: src/math/sort.c:440
 #, c-format
 msgid ""
 "Out of memory.  Could not allocate room for minimum of %d cases of %d bytes "
 #, c-format
 msgid ""
 "Out of memory.  Could not allocate room for minimum of %d cases of %d bytes "
@@ -4271,27 +4271,27 @@ msgstr ""
 msgid "PSPP Data Editor"
 msgstr ""
 
 msgid "PSPP Data Editor"
 msgstr ""
 
-#: src/ui/gui/menu-actions.c:222 src/ui/gui/psppire.glade:430
+#: src/ui/gui/menu-actions.c:220 src/ui/gui/psppire.glade:459
 msgid "Open"
 msgstr ""
 
 msgid "Open"
 msgstr ""
 
-#: src/ui/gui/menu-actions.c:230
+#: src/ui/gui/menu-actions.c:228
 msgid "System Files (*.sav)"
 msgstr ""
 
 msgid "System Files (*.sav)"
 msgstr ""
 
-#: src/ui/gui/menu-actions.c:236
+#: src/ui/gui/menu-actions.c:234
 msgid "Portable Files (*.por) "
 msgstr ""
 
 msgid "Portable Files (*.por) "
 msgstr ""
 
-#: src/ui/gui/menu-actions.c:242
+#: src/ui/gui/menu-actions.c:240
 msgid "All Files"
 msgstr ""
 
 msgid "All Files"
 msgstr ""
 
-#: src/ui/gui/menu-actions.c:274
+#: src/ui/gui/menu-actions.c:272
 msgid "Save Data As"
 msgstr ""
 
 msgid "Save Data As"
 msgstr ""
 
-#: src/ui/gui/menu-actions.c:566
+#: src/ui/gui/menu-actions.c:568
 msgid "Font Selection"
 msgstr ""
 
 msgid "Font Selection"
 msgstr ""
 
@@ -4319,11 +4319,11 @@ msgstr ""
 msgid "Sorry. The help system hasn't yet been implemented."
 msgstr ""
 
 msgid "Sorry. The help system hasn't yet been implemented."
 msgstr ""
 
-#: src/ui/gui/psppire-data-store.c:693
+#: src/ui/gui/psppire-data-store.c:695
 msgid "var"
 msgstr ""
 
 msgid "var"
 msgstr ""
 
-#: src/ui/gui/psppire-data-store.c:777 src/ui/gui/psppire-var-store.c:518
+#: src/ui/gui/psppire-data-store.c:779 src/ui/gui/psppire-var-store.c:518
 #: src/ui/gui/psppire-var-store.c:528 src/ui/gui/psppire-var-store.c:538
 #: src/ui/gui/psppire-var-store.c:735
 #, c-format
 #: src/ui/gui/psppire-var-store.c:528 src/ui/gui/psppire-var-store.c:538
 #: src/ui/gui/psppire-var-store.c:735
 #, c-format
@@ -4338,141 +4338,149 @@ msgstr ""
 msgid "_Edit"
 msgstr ""
 
 msgid "_Edit"
 msgstr ""
 
+#: src/ui/gui/psppire.glade:140
+msgid "Paste _Variables"
+msgstr ""
+
 #: src/ui/gui/psppire.glade:148
 #: src/ui/gui/psppire.glade:148
-msgid "_Insert"
+msgid "Cl_ear"
+msgstr ""
+
+#: src/ui/gui/psppire.glade:165
+msgid "_Find"
 msgstr ""
 
 msgstr ""
 
-#: src/ui/gui/psppire.glade:161
+#: src/ui/gui/psppire.glade:189
 msgid "_View"
 msgstr ""
 
 msgid "_View"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:170
+#: src/ui/gui/psppire.glade:198
 msgid "Status Bar"
 msgstr ""
 
 msgid "Status Bar"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:179
+#: src/ui/gui/psppire.glade:207
 msgid "Toolbars"
 msgstr ""
 
 msgid "Toolbars"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:193
+#: src/ui/gui/psppire.glade:221
 msgid "Fonts"
 msgstr ""
 
 msgid "Fonts"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:202
+#: src/ui/gui/psppire.glade:230
 msgid "Grid Lines"
 msgstr ""
 
 msgid "Grid Lines"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:212 src/ui/gui/psppire.glade:718
-#: src/ui/gui/psppire.glade:1597 src/ui/gui/psppire.glade:1874
+#: src/ui/gui/psppire.glade:240 src/ui/gui/psppire.glade:748
+#: src/ui/gui/psppire.glade:1627 src/ui/gui/psppire.glade:1904
 msgid "Value Labels"
 msgstr ""
 
 msgid "Value Labels"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:228
+#: src/ui/gui/psppire.glade:256
 msgid "Data"
 msgstr ""
 
 msgid "Data"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:237 src/ui/gui/psppire.glade:549
+#: src/ui/gui/psppire.glade:265 src/ui/gui/psppire.glade:578
 msgid "Variables"
 msgstr ""
 
 msgid "Variables"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:250
+#: src/ui/gui/psppire.glade:278
 msgid "_Data"
 msgstr ""
 
 msgid "_Data"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:260 src/ui/gui/psppire.glade:624
+#: src/ui/gui/psppire.glade:288 src/ui/gui/psppire.glade:654
 msgid "Insert Variable"
 msgstr ""
 
 msgid "Insert Variable"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:269
+#: src/ui/gui/psppire.glade:297
 msgid "Insert Cases"
 msgstr ""
 
 msgid "Insert Cases"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:277 src/ui/gui/psppire.glade:533
-#: src/ui/gui/psppire.glade:2439
+#: src/ui/gui/psppire.glade:306 src/ui/gui/psppire.glade:562
+#: src/ui/gui/psppire.glade:2469
 msgid "Go To Case"
 msgstr ""
 
 msgid "Go To Case"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:304 src/ui/gui/psppire.glade:2550
+#: src/ui/gui/psppire.glade:333 src/ui/gui/psppire.glade:2580
 msgid "Sort Cases"
 msgstr ""
 
 msgid "Sort Cases"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:314
+#: src/ui/gui/psppire.glade:343
 msgid "Transpose"
 msgstr ""
 
 msgid "Transpose"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:323
+#: src/ui/gui/psppire.glade:352
 msgid "Restructure"
 msgstr ""
 
 msgid "Restructure"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:332
+#: src/ui/gui/psppire.glade:361
 msgid "Merge Files"
 msgstr ""
 
 msgid "Merge Files"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:341
+#: src/ui/gui/psppire.glade:370
 msgid "Aggregate"
 msgstr ""
 
 msgid "Aggregate"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:356 src/ui/gui/psppire.glade:654
+#: src/ui/gui/psppire.glade:385 src/ui/gui/psppire.glade:684
 msgid "Split File"
 msgstr ""
 
 msgid "Split File"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:365 src/ui/gui/psppire.glade:688
+#: src/ui/gui/psppire.glade:394 src/ui/gui/psppire.glade:718
 msgid "Select Cases"
 msgstr ""
 
 msgid "Select Cases"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:374 src/ui/gui/psppire.glade:671
+#: src/ui/gui/psppire.glade:403 src/ui/gui/psppire.glade:701
 msgid "Weight Cases"
 msgstr ""
 
 msgid "Weight Cases"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:386
+#: src/ui/gui/psppire.glade:415
 msgid "_Help"
 msgstr ""
 
 msgid "_Help"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:395
+#: src/ui/gui/psppire.glade:424
 msgid "_About"
 msgstr ""
 
 msgid "_About"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:446
+#: src/ui/gui/psppire.glade:475
 msgid "Save"
 msgstr ""
 
 msgid "Save"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:462
+#: src/ui/gui/psppire.glade:491
 msgid "Print"
 msgstr ""
 
 msgid "Print"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:490
+#: src/ui/gui/psppire.glade:519
 msgid "Undo"
 msgstr ""
 
 msgid "Undo"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:505
+#: src/ui/gui/psppire.glade:534
 msgid "Redo"
 msgstr ""
 
 msgid "Redo"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:579
+#: src/ui/gui/psppire.glade:608
 msgid "Find"
 msgstr ""
 
 msgid "Find"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:607
+#: src/ui/gui/psppire.glade:636
 msgid "Insert Case"
 msgstr ""
 
 msgid "Insert Case"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:738
+#: src/ui/gui/psppire.glade:768
 msgid "Use Sets"
 msgstr ""
 
 msgid "Use Sets"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:870
+#: src/ui/gui/psppire.glade:900
 msgid "Data View"
 msgstr ""
 
 msgid "Data View"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:918
+#: src/ui/gui/psppire.glade:948
 msgid "Variable View"
 msgstr ""
 
 msgid "Variable View"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:964
+#: src/ui/gui/psppire.glade:994
 msgid "This is pre-alpha software.  It probably will not work."
 msgstr ""
 
 msgid "This is pre-alpha software.  It probably will not work."
 msgstr ""
 
-#: src/ui/gui/psppire.glade:965
+#: src/ui/gui/psppire.glade:995
 msgid ""
 "    This program is free software; you can redistribute it and/or modify\n"
 "    it under the terms of the GNU General Public License as published by\n"
 msgid ""
 "    This program is free software; you can redistribute it and/or modify\n"
 "    it under the terms of the GNU General Public License as published by\n"
@@ -4490,107 +4498,107 @@ msgid ""
 "    02110-1301, USA.\n"
 msgstr ""
 
 "    02110-1301, USA.\n"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:990
+#: src/ui/gui/psppire.glade:1020
 msgid "Variable Type"
 msgstr ""
 
 msgid "Variable Type"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1042 src/ui/gui/psppire-var-store.c:445
+#: src/ui/gui/psppire.glade:1072 src/ui/gui/psppire-var-store.c:445
 msgid "Comma"
 msgstr ""
 
 msgid "Comma"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1062 src/ui/gui/psppire-var-store.c:446
+#: src/ui/gui/psppire.glade:1092 src/ui/gui/psppire-var-store.c:446
 msgid "Dot"
 msgstr ""
 
 msgid "Dot"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1082
+#: src/ui/gui/psppire.glade:1112
 msgid "Scientific notation"
 msgstr ""
 
 msgid "Scientific notation"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1102 src/ui/gui/psppire-var-store.c:448
+#: src/ui/gui/psppire.glade:1132 src/ui/gui/psppire-var-store.c:448
 msgid "Date"
 msgstr ""
 
 msgid "Date"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1122 src/ui/gui/psppire-var-store.c:449
+#: src/ui/gui/psppire.glade:1152 src/ui/gui/psppire-var-store.c:449
 msgid "Dollar"
 msgstr ""
 
 msgid "Dollar"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1142
+#: src/ui/gui/psppire.glade:1172
 msgid "Custom currency"
 msgstr ""
 
 msgid "Custom currency"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1287
+#: src/ui/gui/psppire.glade:1317
 msgid "positive"
 msgstr ""
 
 msgid "positive"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1312
+#: src/ui/gui/psppire.glade:1342
 msgid "negative"
 msgstr ""
 
 msgid "negative"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1341
+#: src/ui/gui/psppire.glade:1371
 msgid "Sample"
 msgstr ""
 
 msgid "Sample"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1419
+#: src/ui/gui/psppire.glade:1449
 msgid "Decimal Places:"
 msgstr ""
 
 msgid "Decimal Places:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1496
+#: src/ui/gui/psppire.glade:1526
 msgid "Width:"
 msgstr ""
 
 msgid "Width:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1715
+#: src/ui/gui/psppire.glade:1745
 msgid "Value:"
 msgstr ""
 
 msgid "Value:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1743
+#: src/ui/gui/psppire.glade:1773
 msgid "Value Label:"
 msgstr ""
 
 msgid "Value Label:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:1959
+#: src/ui/gui/psppire.glade:1989
 msgid "Missing Values"
 msgstr ""
 
 msgid "Missing Values"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2045
+#: src/ui/gui/psppire.glade:2075
 msgid "_No missing values"
 msgstr ""
 
 msgid "_No missing values"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2070
+#: src/ui/gui/psppire.glade:2100
 msgid "_Discrete missing values"
 msgstr ""
 
 msgid "_Discrete missing values"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2199
+#: src/ui/gui/psppire.glade:2229
 msgid "_Range plus one optional discrete missing value"
 msgstr ""
 
 msgid "_Range plus one optional discrete missing value"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2236
+#: src/ui/gui/psppire.glade:2266
 msgid "_Low:"
 msgstr ""
 
 msgid "_Low:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2295
+#: src/ui/gui/psppire.glade:2325
 msgid "_High:"
 msgstr ""
 
 msgid "_High:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2369
+#: src/ui/gui/psppire.glade:2399
 msgid "Di_screte value:"
 msgstr ""
 
 msgid "Di_screte value:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2495
+#: src/ui/gui/psppire.glade:2525
 msgid "Case Number:"
 msgstr ""
 
 msgid "Case Number:"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2667 src/ui/gui/sort-cases-dialog.c:282
+#: src/ui/gui/psppire.glade:2697 src/ui/gui/sort-cases-dialog.c:282
 msgid "Ascending"
 msgstr ""
 
 msgid "Ascending"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2686 src/ui/gui/sort-cases-dialog.c:284
+#: src/ui/gui/psppire.glade:2716 src/ui/gui/sort-cases-dialog.c:284
 msgid "Descending"
 msgstr ""
 
 msgid "Descending"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2707
+#: src/ui/gui/psppire.glade:2737
 msgid "Sort Order"
 msgstr ""
 
 msgid "Sort Order"
 msgstr ""
 
-#: src/ui/gui/psppire.glade:2744
+#: src/ui/gui/psppire.glade:2774
 msgid "Sort by:"
 msgstr ""
 
 msgid "Sort by:"
 msgstr ""
 
index b63a06919273f96a8b5a2c8741aaedfd72bd92cc..7c52887bf884d773171c4b5d3f9fb2e41be6bb44 100644 (file)
@@ -2,15 +2,12 @@
 
 # PSPP
 
 
 # PSPP
 
-include $(top_srcdir)/src/ui/terminal/automake.mk
 include $(top_srcdir)/src/math/automake.mk
 include $(top_srcdir)/src/libpspp/automake.mk
 include $(top_srcdir)/src/data/automake.mk
 include $(top_srcdir)/src/output/automake.mk
 include $(top_srcdir)/src/language/automake.mk
 
 include $(top_srcdir)/src/math/automake.mk
 include $(top_srcdir)/src/libpspp/automake.mk
 include $(top_srcdir)/src/data/automake.mk
 include $(top_srcdir)/src/output/automake.mk
 include $(top_srcdir)/src/language/automake.mk
 
-if WITHGUI
-include $(top_srcdir)/src/ui/gui/automake.mk
-endif
+include $(top_srcdir)/src/ui/automake.mk
 
 AM_CPPFLAGS += -I$(top_srcdir)/src -I$(top_srcdir)/lib -DPKGDATADIR=\"$(pkgdatadir)\"
 
 AM_CPPFLAGS += -I$(top_srcdir)/src -I$(top_srcdir)/lib -DPKGDATADIR=\"$(pkgdatadir)\"
index 0d10921c52e2dec462b75937dbf6021f3ebffb7d..694d35776aa581f190913960d343d2f63ac681b3 100644 (file)
@@ -1,3 +1,9 @@
+Mon Jul 17 18:26:21 WST 2006 John Darrington <john@darrington.wattle.id.au>
+
+       * casefile.c casefile.h: Converted to  an abstract base class.
+       * casefile-private.h fastfile.c fastfile.h: New files.
+       * automake.mk procedure.c scratch-writer.c storage-stream.c
+
 Wed Jul 12 21:02:26 2006  Ben Pfaff  <blp@gnu.org>
 
        * procedure.c (internal_procedure): Create sink_case with only as
 Wed Jul 12 21:02:26 2006  Ben Pfaff  <blp@gnu.org>
 
        * procedure.c (internal_procedure): Create sink_case with only as
index d79828b763f3d110c21d892872fc9e480c986c2f..9cbe39e6212cc3950c6f7b69cfc3b8f5f6584812 100644 (file)
@@ -1,4 +1,3 @@
-## Process this file with automake to produce Makefile.in  -*- makefile -*-
 
 noinst_LIBRARIES += src/data/libdata.a
 
 
 noinst_LIBRARIES += src/data/libdata.a
 
@@ -14,8 +13,11 @@ src_data_libdata_a_SOURCES = \
        src/data/case-source.c \
        src/data/case-source.h \
        src/data/case.c \
        src/data/case-source.c \
        src/data/case-source.h \
        src/data/case.c \
-       src/data/casefile.c \
        src/data/casefile.h \
        src/data/casefile.h \
+       src/data/casefile.c \
+       src/data/casefile-private.h \
+       src/data/fastfile.c \
+       src/data/fastfile.h \
        src/data/case.h \
        src/data/category.c \
        src/data/category.h \
        src/data/case.h \
        src/data/category.c \
        src/data/category.h \
diff --git a/src/data/casefile-private.h b/src/data/casefile-private.h
new file mode 100644 (file)
index 0000000..6e35300
--- /dev/null
@@ -0,0 +1,98 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2004, 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#ifndef CASEFILE_PRIVATE_H
+#define CASEFILE_PRIVATE_H
+
+#include <config.h>
+#include <stdbool.h>
+#include <libpspp/ll.h>
+
+struct ccase;
+struct casereader;
+struct casefile;
+
+struct class_casefile
+{
+  void (*destroy) (struct casefile *) ;
+
+  bool (*error) (const struct casefile *) ;
+
+  size_t (*get_value_cnt) (const struct casefile *) ;
+  unsigned long (*get_case_cnt) (const struct casefile *) ;
+
+  struct casereader * (*get_reader) (const struct casefile *) ; 
+
+  bool (*append) (struct casefile *, const struct ccase *) ;
+
+
+  bool (*in_core) (const struct casefile *) ;
+  bool (*to_disk) (const struct casefile *) ;
+  bool (*sleep) (const struct casefile *) ;
+};
+
+struct casefile
+{
+  const struct class_casefile *class ;   /* Class pointer */
+
+  struct ll_list reader_list ;       /* List of our readers. */
+  struct ll ll ;                    /* Element in the class' list 
+                                      of casefiles. */
+  bool being_destroyed;            /* A destructive reader exists */
+};
+
+
+struct class_casereader
+{
+  struct ccase * (*get_next_case) (struct casereader *);
+
+  unsigned long (*cnum) (const struct casereader *);
+
+  void (*destroy) (struct casereader * r);
+};
+
+
+#define CLASS_CASEREADER(K) ( (struct class_casereader *) K)
+
+struct casereader
+{
+  const struct class_casereader *class;  /* Class pointer */
+
+  struct casefile *cf;   /* The casefile to which this reader belongs */
+  struct ll ll;          /* Element in the casefile's list of readers */
+  bool destructive;      /* True if this reader is destructive */
+};
+
+
+#define CASEFILE(C)        ( (struct casefile *) C)
+#define CONST_CASEFILE(C) ( (const struct casefile *) C)
+
+#define CASEFILEREADER(CR) ((struct casereader *) CR)
+
+
+/* Functions for implementations' use  only */
+
+void casefile_register (struct casefile *cf, 
+                       const struct class_casefile *k);
+
+void casereader_register (struct casefile *cf, 
+                         struct casereader *reader, 
+                         const struct class_casereader *k);
+
+#endif
index 525611bfa140fed8d2adfd09ae9ad70c5c0eabca..95e0388c4e8733ed74b6a3e89ebd780a0dbbe771 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
 /* PSPP - computes sample statistics.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
    02110-1301, USA. */
 
 #include <config.h>
    02110-1301, USA. */
 
 #include <config.h>
-#include "casefile.h"
-#include <assert.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
+#include <stddef.h>
+#include <stdbool.h>
 #include <stdlib.h>
 #include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <libpspp/alloc.h>
+#include <assert.h>
+
 #include "case.h"
 #include "case.h"
-#include <libpspp/compiler.h>
-#include <libpspp/message.h>
-#include "full-read.h"
-#include "full-write.h"
-#include <libpspp/misc.h>
-#include <libpspp/str.h>
-#include "make-file.h"
-#include "settings.h"
-#include "variable.h"
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-
-#define IO_BUF_SIZE (8192 / sizeof (union value))
-
-/* A casefile represents a sequentially accessible stream of
-   immutable cases.
-
-   If workspace allows, a casefile is maintained in memory.  If
-   workspace overflows, then the casefile is pushed to disk.  In
-   either case the interface presented to callers is kept the
-   same.
-
-   The life cycle of a casefile consists of up to three phases:
-
-       1. Writing.  The casefile initially contains no cases.  In
-          this phase, any number of cases may be appended to the
-          end of a casefile.  (Cases are never inserted in the
-          middle or before the beginning of a casefile.)
-
-          Use casefile_append() or casefile_append_xfer() to
-          append a case to a casefile.
-
-       2. Reading.  The casefile may be read sequentially,
-          starting from the beginning, by "casereaders".  Any
-          number of casereaders may be created, at any time,
-          during the reading phase.  Each casereader has an
-          independent position in the casefile.
-
-          Ordinary casereaders may only move forward.  They
-          cannot move backward to arbitrary records or seek
-          randomly.  Cloning casereaders is possible, but it is
-          not yet implemented.
-
-          Use casefile_get_reader() to create a casereader for
-          use in phase 2.  This also transitions from phase 1 to
-          phase 2.  Calling casefile_mode_reader() makes the same
-          transition, without creating a casereader.
-
-          Use casereader_read() or casereader_read_xfer() to read
-          a case from a casereader.  Use casereader_destroy() to
-          discard a casereader when it is no longer needed.
-
-          "Random" casereaders, which support a seek operation,
-          may also be created.  These should not, generally, be
-          used for statistical procedures, because random access
-          is much slower than sequential access.  They are
-          intended for use by the GUI.
-
-       3. Destruction.  This phase is optional.  The casefile is
-          also read with casereaders in this phase, but the
-          ability to create new casereaders is curtailed.
-
-          In this phase, casereaders could still be cloned (once
-          we eventually implement cloning).
-
-          To transition from phase 1 or 2 to phase 3 and create a
-          casereader, call casefile_get_destructive_reader().
-          The same functions apply to the casereader obtained
-          this way as apply to casereaders obtained in phase 2.
-          
-          After casefile_get_destructive_reader() is called, no
-          more casereaders may be created with
-          casefile_get_reader() or
-          casefile_get_destructive_reader().  (If cloning of
-          casereaders were implemented, it would still be
-          possible.)
-
-          The purpose of the limitations applied to casereaders
-          in phase 3 is to allow in-memory casefiles to fully
-          transfer ownership of cases to the casereaders,
-          avoiding the need for extra copies of case data.  For
-          relatively static data sets with many variables, I
-          suspect (without evidence) that this may be a big
-          performance boost.
+#include "casefile.h"
+#include "casefile-private.h"
+
+
+struct ccase;
+
+/* A casefile is an abstract class representing an array of cases.  In
+   general, cases are accessible sequentially,  and are immutable once
+   appended to the casefile.  However some implementations may provide
+   special methods for  case mutation or random access.
+
+   Use casefile_append or casefile_append_xfer to append a case to a
+   casefile. 
+
+   The casefile may be read sequentially,
+   starting from the beginning, by "casereaders".  Any
+   number of casereaders may be created, at any time.
+   Each casereader has an independent position in the casefile.
+
+   Casereaders may only move forward.  They cannot move backward to
+   arbitrary records or seek randomly.  Cloning casereaders is
+   possible, but it is not yet implemented.
+
+   Use casereader_read() or casereader_read_xfer() to read
+   a case from a casereader.  Use casereader_destroy() to
+   discard a casereader when it is no longer needed.
 
    When a casefile is no longer needed, it may be destroyed with
    casefile_destroy().  This function will also destroy any
    remaining casereaders. */
 
 
    When a casefile is no longer needed, it may be destroyed with
    casefile_destroy().  This function will also destroy any
    remaining casereaders. */
 
-/* FIXME: should we implement compression? */
-
-/* In-memory cases are arranged in an array of arrays.  The top
-   level is variable size and the size of each bottom level array
-   is fixed at the number of cases defined here.  */
-#define CASES_PER_BLOCK 128             
-
-/* A casefile. */
-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_acct_size;              /* Case size for accounting. */
-    unsigned long case_cnt;             /* Number of cases stored. */
-    enum { MEMORY, DISK } storage;      /* Where cases are stored. */
-    enum { WRITE, READ } mode;          /* Is writing or reading allowed? */
-    struct casereader *readers;         /* List of our readers. */
-    bool being_destroyed;               /* Does a destructive reader exist? */
-    bool ok;                            /* False after I/O error. */
-
-    /* Memory storage. */
-    struct ccase **cases;               /* Pointer to array of cases. */
-
-    /* Disk storage. */
-    int fd;                             /* File descriptor, -1 if none. */
-    char *file_name;                    /* File name. */
-    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. */
-struct casereader 
-  {
-    struct casereader *next, *prev;     /* Next, prev in casefile's list. */
-    struct casefile *cf;                /* Our casefile. */
-    unsigned long case_idx;             /* Case number of current case. */
-    bool destructive;                   /* Is this a destructive reader? */
-    bool random;                        /* Is this a random reader? */
-
-    /* Disk storage. */
-    int fd;                             /* File descriptor. */
-    off_t file_ofs;                     /* Current position in fd. */
-    off_t buffer_ofs;                   /* File offset of buffer start. */
-    union value *buffer;                /* I/O buffer. */
-    size_t buffer_pos;                  /* Offset of buffer position. */
-    struct ccase c;                     /* Current case. */
-  };
+static struct ll_list all_casefiles = LL_INITIALIZER (all_casefiles);
 
 
-/* Return the case number of the current case */
-unsigned long
-casereader_cnum(const struct casereader *r)
+static struct casefile *
+ll_to_casefile (const struct ll *ll)
 {
 {
-  return r->case_idx;
+  return ll_data (ll, struct casefile, ll);
 }
 
 }
 
-/* Doubly linked list of all casefiles. */
-static struct casefile *casefiles;
-
-/* Number of bytes of case allocated in in-memory casefiles. */
-static size_t case_bytes;
-
-static void register_atexit (void);
-static void exit_handler (void);
-
-static void reader_open_file (struct casereader *);
-static void write_case_to_disk (struct casefile *, const struct ccase *);
-static void flush_buffer (struct casefile *);
-static void seek_and_fill_buffer (struct casereader *);
-static bool fill_buffer (struct casereader *);
+static struct casereader *
+ll_to_casereader (const struct ll *ll)
+{
+  return ll_data (ll, struct casereader, ll);
+}
 
 
-static void io_error (struct casefile *, const char *, ...)
-     PRINTF_FORMAT (2, 3);
-static int safe_open (const char *file_name, int flags);
-static int safe_close (int fd);
 
 
-/* Creates and returns a casefile to store cases of VALUE_CNT
-   `union value's each. */
-struct casefile *
-casefile_create (size_t value_cnt
+/* atexit() handler that closes and deletes our temporary
+   files. */
+static void
+exit_handler (void
 {
 {
-  struct casefile *cf = xmalloc (sizeof *cf);
-  cf->next = casefiles;
-  cf->prev = NULL;
-  if (cf->next != NULL)
-    cf->next->prev = cf;
-  casefiles = cf;
-  cf->value_cnt = value_cnt;
-  cf->case_acct_size = (cf->value_cnt + 4) * sizeof *cf->buffer;
-  cf->case_cnt = 0;
-  cf->storage = MEMORY;
-  cf->mode = WRITE;
-  cf->readers = NULL;
-  cf->being_destroyed = 0;
-  cf->ok = true;
-  cf->cases = NULL;
-  cf->fd = -1;
-  cf->file_name = NULL;
-  cf->buffer = NULL;
-  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;
+  while (!ll_is_empty (&all_casefiles))
+    casefile_destroy (ll_to_casefile (ll_head (&all_casefiles)));
 }
 
 }
 
-/* Destroys casefile CF. */
+/* Insert CF into the global list of casefiles */
 void
 void
-casefile_destroy (struct casefile *cf) 
+casefile_register (struct casefile *cf, const struct class_casefile *class)
 {
 {
-  if (cf != NULL) 
+  static bool initialised ;
+  if ( !initialised ) 
     {
     {
-      if (cf->next != NULL)
-        cf->next->prev = cf->prev;
-      if (cf->prev != NULL)
-        cf->prev->next = cf->next;
-      if (casefiles == cf)
-        casefiles = cf->next;
-
-      while (cf->readers != NULL) 
-        casereader_destroy (cf->readers);
-
-      if (cf->cases != NULL) 
-        {
-          size_t idx, block_cnt;
-
-          case_bytes -= cf->case_cnt * cf->case_acct_size;
-          for (idx = 0; idx < cf->case_cnt; idx++)
-            {
-              size_t block_idx = idx / CASES_PER_BLOCK;
-              size_t case_idx = idx % CASES_PER_BLOCK;
-              struct ccase *c = &cf->cases[block_idx][case_idx];
-              case_destroy (c);
-            }
-
-          block_cnt = DIV_RND_UP (cf->case_cnt, CASES_PER_BLOCK);
-          for (idx = 0; idx < block_cnt; idx++)
-            free (cf->cases[idx]);
-
-          free (cf->cases);
-        }
-
-      if (cf->fd != -1)
-        safe_close (cf->fd);
-          
-      if (cf->file_name != NULL && remove (cf->file_name) == -1) 
-        io_error (cf, _("%s: Removing temporary file: %s."),
-                  cf->file_name, strerror (errno));
-      free (cf->file_name);
-
-      free (cf->buffer);
-
-      free (cf);
+      atexit (exit_handler);
+      initialised = true;
     }
     }
+
+  cf->class = class;
+  ll_push_head (&all_casefiles, &cf->ll);
+  ll_init (&cf->reader_list);
 }
 
 }
 
-/* Returns true if an I/O error has occurred in casefile CF. */
-bool
-casefile_error (const struct casefile *cf) 
+/* Remove CF from the global list */
+static void
+casefile_unregister(struct casefile *cf)
 {
 {
-  return !cf->ok;
+  ll_remove (&cf->ll);
 }
 
 }
 
-/* Returns true only if casefile CF is stored in memory (instead of on
-   disk), false otherwise. */
-bool
-casefile_in_core (const struct casefile *cf) 
+/* Return the casefile corresponding to this reader */
+struct casefile *
+casereader_get_casefile (const struct casereader *r)
 {
 {
-  assert (cf != NULL);
-
-  return cf->storage == MEMORY;
+  return r->cf;
 }
 
 }
 
-/* Puts a casefile to "sleep", that is, minimizes the resources
-   needed for it by closing its file descriptor and freeing its
-   buffer.  This is useful if we need so many casefiles that we
-   might not have enough memory and file descriptors to go
-   around.
+/* Return the case number of the current case */
+unsigned long
+casereader_cnum(const struct casereader *r)
+{
+  return r->class->cnum(r);
+}
 
 
-   For simplicity, this implementation always converts the
-   casefile to reader mode.  If this turns out to be a problem,
-   with a little extra work we could also support sleeping
-   writers.
 
 
-   Returns true if successful, false if an I/O error occurred. */
+/* Reads a copy of the next case from READER into C.
+   Caller is responsible for destroying C.
+   Returns true if successful, false at end of file. */
 bool
 bool
-casefile_sleep (const struct casefile *cf_) 
+casereader_read (struct casereader *reader, struct ccase *c)
 {
 {
-  struct casefile *cf = (struct casefile *) cf_;
-  assert (cf != NULL);
+  struct casefile *cf = casereader_get_casefile (reader);
 
 
-  casefile_mode_reader (cf);
-  casefile_to_disk (cf);
-  flush_buffer (cf);
+  struct ccase *read_case = NULL;
 
 
-  if (cf->fd != -1) 
-    {
-      safe_close (cf->fd);
-      cf->fd = -1;
-    }
-  if (cf->buffer != NULL) 
-    {
-      free (cf->buffer);
-      cf->buffer = NULL;
-    }
+  if ( casefile_error (cf) )
+    return false;
 
 
-  return cf->ok;
-}
+  read_case = reader->class->get_next_case (reader);
+  if ( ! read_case ) return false;
 
 
-/* Returns the number of `union value's in a case for CF. */
-size_t
-casefile_get_value_cnt (const struct casefile *cf) 
-{
-  assert (cf != NULL);
+  case_clone (c, read_case );
 
 
-  return cf->value_cnt;
+  return true;
 }
 
 }
 
-/* Returns the number of cases in casefile CF. */
-unsigned long
-casefile_get_case_cnt (const struct casefile *cf) 
-{
-  assert (cf != NULL);
-
-  return cf->case_cnt;
-}
 
 
-/* Appends a copy of case C to casefile CF.  Not valid after any
-   reader for CF has been created.
-   Returns true if successful, false if an I/O error occurred. */
+/* Reads the next case from READER into C and transfers ownership
+   to the caller.  Caller is responsible for destroying C.
+   Returns true if successful, false at end of file or on I/O
+   error. */
 bool
 bool
-casefile_append (struct casefile *cf, const struct ccase *c) 
+casereader_read_xfer (struct casereader *ffr, struct ccase *c)
 {
 {
-  assert (cf != NULL);
-  assert (c != NULL);
-  assert (cf->mode == WRITE);
+  struct casefile *cf = casereader_get_casefile (ffr);
 
 
-  assert ( cf->value_cnt <= c->case_data->value_cnt );
+  struct ccase *read_case = NULL ;
 
 
-  /* Try memory first. */
-  if (cf->storage == MEMORY) 
-    {
-      if (case_bytes < get_workspace ())
-        {
-          size_t block_idx = cf->case_cnt / CASES_PER_BLOCK;
-          size_t case_idx = cf->case_cnt % CASES_PER_BLOCK;
-          struct ccase new_case;
-
-          case_bytes += cf->case_acct_size;
-          case_clone (&new_case, c);
-          if (case_idx == 0) 
-            {
-              if ((block_idx & (block_idx - 1)) == 0) 
-                {
-                  size_t block_cap = block_idx == 0 ? 1 : block_idx * 2;
-                  cf->cases = xnrealloc (cf->cases,
-                                         block_cap, sizeof *cf->cases);
-                }
-
-              cf->cases[block_idx] = xnmalloc (CASES_PER_BLOCK,
-                                               sizeof **cf->cases);
-            }
-
-          case_move (&cf->cases[block_idx][case_idx], &new_case);
-        }
-      else
-        {
-          casefile_to_disk (cf);
-          assert (cf->storage == DISK);
-          write_case_to_disk (cf, c);
-        }
-    }
+  if ( casefile_error (cf) )
+    return false;
+
+  read_case = ffr->class->get_next_case (ffr);
+  if ( ! read_case ) return false;
+
+  if ( ffr->destructive && casefile_in_core (cf) )
+    case_move (c, read_case);
   else
   else
-    write_case_to_disk (cf, c);
+    case_clone (c, read_case);
 
 
-  cf->case_cnt++;
-  return cf->ok;
+  return true;
 }
 
 }
 
-/* Appends case C to casefile CF, which takes over ownership of
-   C.  Not valid after any reader for CF has been created.
-   Returns true if successful, false if an I/O error occurred. */
-bool
-casefile_append_xfer (struct casefile *cf, struct ccase *c) 
+/* Destroys R. */
+void 
+casereader_destroy (struct casereader *r)
 {
 {
-  casefile_append (cf, c);
-  case_destroy (c);
-  return cf->ok;
+  ll_remove (&r->ll);
+
+  r->class->destroy(r);
 }
 
 }
 
-/* Writes case C to casefile CF's disk buffer, first flushing the buffer to
-   disk if it would otherwise overflow.
-   Returns true if successful, false if an I/O error occurred. */
-static void
-write_case_to_disk (struct casefile *cf, const struct ccase *c) 
+/* Destroys casefile CF. */
+void
+casefile_destroy(struct casefile *cf)
 {
 {
-  if (!cf->ok)
-    return;
+  if (!cf) return;
   
   
-  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);
+  assert(cf->class->destroy);
+
+  while (!ll_is_empty (&cf->reader_list))
+    casereader_destroy (ll_to_casereader (ll_head (&cf->reader_list)));
+      
+  casefile_unregister(cf);
+
+  cf->class->destroy(cf);
 }
 
 }
 
-/* If any bytes in CF's output buffer are used, flush them to
-   disk. */
-static void
-flush_buffer (struct casefile *cf) 
+/* Returns true if an I/O error has occurred in casefile CF. */
+bool 
+casefile_error (const struct casefile *cf)
 {
 {
-  if (cf->ok && cf->buffer_used > 0) 
-    {
-      if (!full_write (cf->fd, cf->buffer,
-                       cf->buffer_size * sizeof *cf->buffer))
-        io_error (cf, _("Error writing temporary file: %s."),
-                  strerror (errno));
-      cf->buffer_used = 0;
-    }
+  return cf->class->error(cf);
 }
 
 }
 
-/* If CF is currently stored in memory, writes it to disk.  Readers, if any,
-   retain their current positions.
-   Returns true if successful, false if an I/O error occurred. */
-bool
-casefile_to_disk (const struct casefile *cf_) 
+/* Returns the number of cases in casefile CF. */
+unsigned long 
+casefile_get_case_cnt (const struct casefile *cf)
 {
 {
-  struct casefile *cf = (struct casefile *) cf_;
-  struct casereader *reader;
-  
-  assert (cf != NULL);
-
-  if (cf->storage == MEMORY)
-    {
-      size_t idx, block_cnt;
-      
-      assert (cf->file_name == NULL);
-      assert (cf->fd == -1);
-      assert (cf->buffer_used == 0);
-
-      if (!make_temp_file (&cf->fd, &cf->file_name))
-        {
-          cf->ok = false;
-          return false;
-        }
-      cf->storage = DISK;
-
-      cf->buffer = xnmalloc (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++)
-        {
-          size_t block_idx = idx / CASES_PER_BLOCK;
-          size_t case_idx = idx % CASES_PER_BLOCK;
-          struct ccase *c = &cf->cases[block_idx][case_idx];
-          write_case_to_disk (cf, c);
-          case_destroy (c);
-        }
-
-      block_cnt = DIV_RND_UP (cf->case_cnt, CASES_PER_BLOCK);
-      for (idx = 0; idx < block_cnt; idx++)
-        free (cf->cases[idx]);
-
-      free (cf->cases);
-      cf->cases = NULL;
-
-      if (cf->mode == READ)
-        flush_buffer (cf);
-
-      for (reader = cf->readers; reader != NULL; reader = reader->next)
-        reader_open_file (reader);
-    }
-  return cf->ok;
+  return cf->class->get_case_cnt(cf);
 }
 
 }
 
-/* Changes CF to reader mode, ensuring that no more cases may be
-   added.  Creating a casereader for CF has the same effect. */
-void
-casefile_mode_reader (struct casefile *cf) 
+/* Returns the number of `union value's in a case for CF. */
+size_t 
+casefile_get_value_cnt (const struct casefile *cf)
 {
 {
-  assert (cf != NULL);
-  cf->mode = READ;
+  return cf->class->get_value_cnt(cf);
 }
 
 /* Creates and returns a casereader for CF.  A casereader can be used to
    sequentially read the cases in a casefile. */
 struct casereader *
 }
 
 /* Creates and returns a casereader for CF.  A casereader can be used to
    sequentially read the cases in a casefile. */
 struct casereader *
-casefile_get_reader (const struct casefile *cf_) 
+casefile_get_reader (const struct casefile *cf)
 {
 {
-  struct casefile *cf = (struct casefile *) cf_;
-  struct casereader *reader;
-
-  assert (cf != NULL);
-  assert (!cf->being_destroyed);
+  struct casereader *r = cf->class->get_reader(cf);
+  r->cf = (struct casefile *) cf;
 
 
-  /* Flush the buffer to disk if it's not empty. */
-  if (cf->mode == WRITE && cf->storage == DISK)
-    flush_buffer (cf);
+  assert (r->class);
   
   
-  cf->mode = READ;
-
-  reader = xmalloc (sizeof *reader);
-  reader->next = cf->readers;
-  if (cf->readers != NULL)
-    reader->next->prev = reader;
-  cf->readers = reader;
-  reader->prev = NULL;
-  reader->cf = cf;
-  reader->case_idx = 0;
-  reader->destructive = 0;
-  reader->random = false;
-  reader->fd = -1;
-  reader->buffer = NULL;
-  reader->buffer_pos = 0;
-  case_nullify (&reader->c);
-
-  if (reader->cf->storage == DISK) 
-    reader_open_file (reader);
-
-  return reader;
-}
-
-/* Creates and returns a random casereader for CF.  A random
-   casereader can be used to randomly read the cases in a
-   casefile. */
-struct casereader *
-casefile_get_random_reader (const struct casefile *cf) 
-{
-  struct casefile  *mutable_casefile = (struct casefile*) cf;
-  struct casereader *reader;
-
-  enum { WRITE, READ } mode = cf->mode ;
-  reader = casefile_get_reader (cf);
-  reader->random = true;
-  mutable_casefile->mode = mode;
-  
-  return reader;
+  return r;
 }
 
 /* Creates and returns a destructive casereader for CF.  Like a
 }
 
 /* Creates and returns a destructive casereader for CF.  Like a
@@ -568,303 +232,84 @@ casefile_get_random_reader (const struct casefile *cf)
 struct casereader *
 casefile_get_destructive_reader (struct casefile *cf) 
 {
 struct casereader *
 casefile_get_destructive_reader (struct casefile *cf) 
 {
-  struct casereader *reader;
-  
-  assert (cf->readers == NULL);
-  reader = casefile_get_reader (cf);
-  reader->destructive = 1;
-  cf->being_destroyed = 1;
-  return reader;
-}
-
-/* Opens a disk file for READER and seeks to the current position as indicated
-   by case_idx.  Normally the current position is the beginning of the file,
-   but casefile_to_disk may cause the file to be opened at a different
-   position. */
-static void
-reader_open_file (struct casereader *reader) 
-{
-  struct casefile *cf = reader->cf;
-  if (!cf->ok || reader->case_idx >= cf->case_cnt)
-    return;
-
-  if (cf->fd != -1) 
-    {
-      reader->fd = cf->fd;
-      cf->fd = -1;
-    }
-  else 
-    {
-      reader->fd = safe_open (cf->file_name, O_RDONLY);
-      if (reader->fd < 0)
-        io_error (cf, _("%s: Opening temporary file: %s."),
-                  cf->file_name, strerror (errno));
-    }
-
-  if (cf->buffer != NULL) 
-    {
-      reader->buffer = cf->buffer;
-      cf->buffer = NULL; 
-    }
-  else 
-    {
-      reader->buffer = xnmalloc (cf->buffer_size, sizeof *cf->buffer);
-      memset (reader->buffer, 0, cf->buffer_size * sizeof *cf->buffer); 
-    }
-
-  case_create (&reader->c, cf->value_cnt);
-
-  reader->buffer_ofs = -1;
-  reader->file_ofs = -1;
-  seek_and_fill_buffer (reader);
-}
-
-/* Seeks the backing file for READER to the proper position and
-   refreshes the buffer contents. */
-static void
-seek_and_fill_buffer (struct casereader *reader) 
-{
-  struct casefile *cf = reader->cf;
-  off_t new_ofs;
-
-  if (cf->value_cnt != 0) 
-    {
-      size_t buffer_case_cnt = cf->buffer_size / cf->value_cnt;
-      new_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->value_cnt);
-    }
-  else 
-    new_ofs = 0;
-  if (new_ofs != reader->file_ofs) 
-    {
-      if (lseek (reader->fd, new_ofs, SEEK_SET) != new_ofs)
-        io_error (cf, _("%s: Seeking temporary file: %s."),
-                  cf->file_name, strerror (errno));
-      else
-        reader->file_ofs = new_ofs;
-    }
+  struct casereader *r = cf->class->get_reader (cf);
+  r->cf = cf;
+  r->destructive = true;
+  cf->being_destroyed = true;
 
 
-  if (cf->case_cnt > 0 && cf->value_cnt > 0 && reader->buffer_ofs != new_ofs)
-    fill_buffer (reader);
+  return r;
 }
 
 }
 
-/* Fills READER's buffer by reading a block from disk. */
-static bool
-fill_buffer (struct casereader *reader)
-{
-  if (reader->cf->ok) 
-    {
-      int bytes = full_read (reader->fd, reader->buffer,
-                             reader->cf->buffer_size * sizeof *reader->buffer);
-      if (bytes < 0) 
-        io_error (reader->cf, _("%s: Reading temporary file: %s."),
-                  reader->cf->file_name, strerror (errno));
-      else if (bytes != reader->cf->buffer_size * sizeof *reader->buffer) 
-        io_error (reader->cf, _("%s: Temporary file ended unexpectedly."),
-                  reader->cf->file_name);
-      else 
-        {
-          reader->buffer_ofs = reader->file_ofs;
-          reader->file_ofs += bytes; 
-        }
-    }
-  return reader->cf->ok;
-}
-
-/* Returns the casefile that READER reads. */
-const struct casefile *
-casereader_get_casefile (const struct casereader *reader) 
-{
-  assert (reader != NULL);
-  
-  return reader->cf;
-}
-
-/* Reads a copy of the next case from READER into C.
-   Caller is responsible for destroying C.
-   Returns true if successful, false at end of file. */
-bool
-casereader_read (struct casereader *reader, struct ccase *c) 
+/* Appends a copy of case C to casefile CF. 
+   Returns true if successful, false if an I/O error occurred. */
+bool 
+casefile_append (struct casefile *cf, const struct ccase *c)
 {
 {
-  assert (reader != NULL);
-  
-  if (!reader->cf->ok || reader->case_idx >= reader->cf->case_cnt) 
-    return false;
+  assert (c->case_data->value_cnt >= casefile_get_value_cnt (cf));
 
 
-  if (reader->cf->storage == MEMORY) 
-    {
-      size_t block_idx = reader->case_idx / CASES_PER_BLOCK;
-      size_t case_idx = reader->case_idx % CASES_PER_BLOCK;
-
-      case_clone (c, &reader->cf->cases[block_idx][case_idx]);
-      reader->case_idx++;
-      return true;
-    }
-  else 
-    {
-      if (reader->buffer_pos + reader->cf->value_cnt > reader->cf->buffer_size)
-        {
-          if (!fill_buffer (reader))
-            return false;
-          reader->buffer_pos = 0;
-        }
-
-      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);
-      return true;
-    }
+  return cf->class->append(cf, c);
 }
 
 }
 
-/* Reads the next case from READER into C and transfers ownership
-   to the caller.  Caller is responsible for destroying C.
-   Returns true if successful, false at end of file or on I/O
-   error. */
-bool
-casereader_read_xfer (struct casereader *reader, struct ccase *c)
+/* Appends case C to casefile CF, which takes over ownership of
+   C.  
+   Returns true if successful, false if an I/O error occurred. */
+bool 
+casefile_append_xfer (struct casefile *cf, struct ccase *c)
 {
 {
-  assert (reader != NULL);
-
-  if (reader->destructive == 0
-      || reader->case_idx >= reader->cf->case_cnt
-      || reader->cf->storage == DISK) 
-    return casereader_read (reader, c);
-  else 
-    {
-      size_t block_idx = reader->case_idx / CASES_PER_BLOCK;
-      size_t case_idx = reader->case_idx % CASES_PER_BLOCK;
-      struct ccase *read_case = &reader->cf->cases[block_idx][case_idx];
-
-      case_move (c, read_case);
-      reader->case_idx++;
-      return true;
-    }
-}
+  assert (c->case_data->value_cnt >= casefile_get_value_cnt (cf));
 
 
-/* Sets the next case to be read by READER to CASE_IDX,
-   which must be less than the number of cases in the casefile.
-   Allowed only for random readers. */
-void
-casereader_seek (struct casereader *reader, unsigned long case_idx) 
-{
-  assert (reader != NULL);
-  assert (reader->random);
-  assert (case_idx < reader->cf->case_cnt);
+  cf->class->append (cf, c);
+  case_destroy (c);
 
 
-  reader->case_idx = case_idx;
-  if (reader->cf->storage == DISK)
-    seek_and_fill_buffer (reader);
+  return cf->class->error (cf);
 }
 
 }
 
-/* Destroys READER. */
-void
-casereader_destroy (struct casereader *reader)
-{
-  assert (reader != NULL);
 
 
-  if (reader->next != NULL)
-    reader->next->prev = reader->prev;
-  if (reader->prev != NULL)
-    reader->prev->next = reader->next;
-  if (reader->cf->readers == reader)
-    reader->cf->readers = reader->next;
 
 
-  if (reader->cf->buffer == NULL)
-    reader->cf->buffer = reader->buffer;
-  else
-    free (reader->buffer);
 
 
-  if (reader->fd != -1) 
-    {
-      if (reader->cf->fd == -1)
-        reader->cf->fd = reader->fd;
-      else
-        safe_close (reader->fd);
-    }
+/* Puts a casefile to "sleep", that is, minimizes the resources
+   needed for it by closing its file descriptor and freeing its
+   buffer.  This is useful if we need so many casefiles that we
+   might not have enough memory and file descriptors to go
+   around.
   
   
-  case_destroy (&reader->c);
+   Implementations may choose to silently ignore this function.
 
 
-  free (reader);
-}
-
-/* Marks CF as having encountered an I/O error.
-   If this is the first error on CF, reports FORMAT to the user,
-   doing printf()-style substitutions. */
-static void
-io_error (struct casefile *cf, const char *format, ...)
+   Returns true if successful, false if an I/O error occurred. */
+bool
+casefile_sleep (const struct casefile *cf)
 {
 {
-  if (cf->ok) 
-    {
-      struct msg m;
-      va_list args;
-
-      m.category = MSG_GENERAL;
-      m.severity = MSG_ERROR;
-      m.where.file_name = NULL;
-      m.where.line_number = -1;
-      va_start (args, format);
-      m.text = xvasprintf (format, args);
-      va_end (args);
-      
-      msg_emit (&m);
-    }
-  cf->ok = false;
+  return cf->class->sleep ? cf->class->sleep(cf) : true;
 }
 
 }
 
-/* Calls open(), passing FILE_NAME and FLAGS, repeating as necessary
-   to deal with interrupted calls. */
-static int
-safe_open (const char *file_name, int flags) 
+/* Returns true only if casefile CF is stored in memory (instead of on
+   disk), false otherwise. 
+*/
+bool
+casefile_in_core (const struct casefile *cf)
 {
 {
-  int fd;
-
-  do 
-    {
-      fd = open (file_name, flags);
-    }
-  while (fd == -1 && errno == EINTR);
-
-  return fd;
+  return cf->class->in_core(cf);
 }
 
 }
 
-/* Calls close(), passing FD, repeating as necessary to deal with
-   interrupted calls. */
-static int safe_close (int fd) 
-{
-  int retval;
-
-  do 
-    {
-      retval = close (fd);
-    }
-  while (retval == -1 && errno == EINTR);
+/* If CF is currently stored in memory, writes it to disk.  Readers, if any,
+   retain their current positions.
 
 
-  return retval;
-}
+   Implementations may choose to silently ignore this function.
 
 
-/* Registers our exit handler with atexit() if it has not already
-   been registered. */
-static void
-register_atexit (void) 
+   Returns true if successful, false if an I/O error occurred. */
+bool 
+casefile_to_disk (const struct casefile *cf)
 {
 {
-  static bool registered = false;
-  if (!registered) 
-    {
-      registered = true;
-      atexit (exit_handler);
-    }
+  return cf->class->to_disk ? cf->class->to_disk(cf) : true;
 }
 
 }
 
-/* atexit() handler that closes and deletes our temporary
-   files. */
-static void
-exit_handler (void) 
+void
+casereader_register(struct casefile *cf, 
+                   struct casereader *reader, 
+                   const struct class_casereader *class)
 {
 {
-  while (casefiles != NULL)
-    casefile_destroy (casefiles);
+  reader->class = class;
+  reader->cf = cf;
+      
+  ll_push_head (&cf->reader_list, &reader->ll);
 }
 }
index ab60e192798cbea20d0a142cb2ef06727e03c492..1bddb2d24a19769224ad42ee8a85db56a8744538 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - computes sample statistics.
 /* PSPP - computes sample statistics.
-   Copyright (C) 2004 Free Software Foundation, Inc.
+   Copyright (C) 2004, 2006 Free Software Foundation, Inc.
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
    Written by Ben Pfaff <blp@gnu.org>.
 
    This program is free software; you can redistribute it and/or
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA. */
 
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
    02110-1301, USA. */
 
-#ifndef HEADER_CASEFILE
-#define HEADER_CASEFILE
+#ifndef CASEFILE_H
+#define CASEFILE_H
 
 
+#include <config.h>
 #include <stddef.h>
 #include <stdbool.h>
 
 #include <stddef.h>
 #include <stdbool.h>
 
+
 struct ccase;
 struct ccase;
-struct casefile;
 struct casereader;
 struct casereader;
+struct casefile;
+
+
+
+struct casefile *casereader_get_casefile (const struct casereader *r);
+
+unsigned long casereader_cnum (const struct casereader *r);
+
+bool casereader_read (struct casereader *r, struct ccase *c);
+
+bool casereader_read_xfer (struct casereader *r, struct ccase *c);
+
+void casereader_destroy (struct casereader *r);
+
+void casefile_destroy (struct casefile *cf);
+
+bool casefile_error (const struct casefile *cf);
+
+unsigned long casefile_get_case_cnt (const struct casefile *cf);
+
+size_t casefile_get_value_cnt (const struct casefile *cf);
 
 
-struct casefile *casefile_create (size_t value_cnt);
-void casefile_destroy (struct casefile *);
+struct casereader *casefile_get_reader (const struct casefile *cf);
+struct casereader *casefile_get_destructive_reader (struct casefile *cf);
 
 
-bool casefile_error (const struct casefile *);
-bool casefile_in_core (const struct casefile *);
-bool casefile_to_disk (const struct casefile *);
-bool casefile_sleep (const struct casefile *);
 
 
-size_t casefile_get_value_cnt (const struct casefile *);
-unsigned long casefile_get_case_cnt (const struct casefile *);
 
 
-bool casefile_append (struct casefile *, const struct ccase *);
-bool casefile_append_xfer (struct casefile *, struct ccase *);
+bool casefile_append (struct casefile *cf, const struct ccase *c);
 
 
-void casefile_mode_reader (struct casefile *);
-struct casereader *casefile_get_reader (const struct casefile *);
-struct casereader *casefile_get_destructive_reader (struct casefile *);
-struct casereader *casefile_get_random_reader (const struct casefile *);
+bool casefile_append_xfer (struct casefile *cf, struct ccase *c);
 
 
-const struct casefile *casereader_get_casefile (const struct casereader *);
-bool casereader_read (struct casereader *, struct ccase *);
-bool casereader_read_xfer (struct casereader *, struct ccase *);
-void casereader_destroy (struct casereader *);
+bool casefile_sleep (const struct casefile *cf);
 
 
-void casereader_seek (struct casereader *, unsigned long case_idx);
+bool casefile_in_core (const struct casefile *cf);
 
 
-unsigned long casereader_cnum(const struct casereader *);
+bool casefile_to_disk (const struct casefile *cf);
 
 
-#endif /* casefile.h */
+#endif
diff --git a/src/data/fastfile.c b/src/data/fastfile.c
new file mode 100644 (file)
index 0000000..9cdba15
--- /dev/null
@@ -0,0 +1,751 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2004, 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#include <config.h>
+#include "casefile.h"
+#include "casefile-private.h"
+#include "fastfile.h"
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libpspp/alloc.h>
+#include "case.h"
+#include <libpspp/compiler.h>
+#include <libpspp/message.h>
+#include "full-read.h"
+#include "full-write.h"
+#include <libpspp/misc.h>
+#include "make-file.h"
+#include "settings.h"
+#include "variable.h"
+
+#include "gettext.h"
+#define _(msgid) gettext (msgid)
+
+#define IO_BUF_SIZE (8192 / sizeof (union value))
+
+/* A fastfile represents a sequentially accessible stream of
+   immutable cases.
+
+   If workspace allows, a fastfile is maintained in memory.  If
+   workspace overflows, then the fastfile is pushed to disk.  In
+   either case the interface presented to callers is kept the
+   same.
+
+   The life cycle of a fastfile consists of up to three phases:
+
+       1. Writing.  The fastfile initially contains no cases.  In
+          this phase, any number of cases may be appended to the
+          end of a fastfile.  (Cases are never inserted in the
+          middle or before the beginning of a fastfile.)
+
+          Use casefile_append or casefile_append_xfer to
+          append a case to a fastfile.
+
+       2. Reading.  The fastfile may be read sequentially,
+          starting from the beginning, by "casereaders".  Any
+          number of casereaders may be created, at any time,
+          during the reading phase.  Each casereader has an
+          independent position in the fastfile.
+
+          Ordinary casereaders may only move forward.  They
+          cannot move backward to arbitrary records or seek
+          randomly.  Cloning casereaders is possible, but it is
+          not yet implemented.
+
+          Use casefile_get_reader to create a casereader for
+          use in phase 2.  This also transitions from phase 1 to
+          phase 2.  Calling fastfile_mode_reader makes the same
+          transition, without creating a casereader.
+
+          Use casereader_read or casereader_read_xfer to read
+          a case from a casereader.  Use casereader_destroy to
+          discard a casereader when it is no longer needed.
+
+       3. Destruction.  This phase is optional.  The fastfile is
+          also read with casereaders in this phase, but the
+          ability to create new casereaders is curtailed.
+
+          In this phase, casereaders could still be cloned (once
+          we eventually implement cloning).
+
+          To transition from phase 1 or 2 to phase 3 and create a
+          casereader, call casefile_get_destructive_reader().
+          The same functions apply to the casereader obtained
+          this way as apply to casereaders obtained in phase 2.
+          
+          After casefile_get_destructive_reader is called, no
+          more casereaders may be created.  (If cloning of
+          casereaders were implemented, it would still be
+          possible.)
+
+          The purpose of the limitations applied to casereaders
+          in phase 3 is to allow in-memory fastfiles to fully
+          transfer ownership of cases to the casereaders,
+          avoiding the need for extra copies of case data.  For
+          relatively static data sets with many variables, I
+          suspect (without evidence) that this may be a big
+          performance boost.
+
+   When a fastfile is no longer needed, it may be destroyed with
+   casefile_destroy.  This function will also destroy any
+   remaining casereaders. */
+
+/* FIXME: should we implement compression? */
+
+/* In-memory cases are arranged in an array of arrays.  The top
+   level is variable size and the size of each bottom level array
+   is fixed at the number of cases defined here.  */
+#define CASES_PER_BLOCK 128
+
+static const struct class_casefile class;
+
+/* A fastfile. */
+struct fastfile
+{
+  struct casefile cf;          /* Parent */
+
+  size_t value_cnt;            /* Case size in `union value's. */
+  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. */
+  enum { WRITE, READ } mode;           /* Is writing or reading allowed? */
+
+  bool ok;                     /* False after I/O error. */
+
+  /* Memory storage. */
+  struct ccase **cases;                /* Pointer to array of cases. */
+
+  /* Disk storage. */
+  int fd;                      /* File descriptor, -1 if none. */
+  char *file_name;             /* File name. */
+  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. */
+};
+
+
+static const struct class_casereader class_reader;
+
+/* For reading out the cases in a fastfile. */
+struct fastfilereader
+{
+  struct casereader cr;                /* Parent */
+
+  unsigned long case_idx;      /* Case number of current case. */
+
+  /* Disk storage. */
+  int fd;                      /* File descriptor. */
+  off_t file_ofs;              /* Current position in fd. */
+  off_t buffer_ofs;            /* File offset of buffer start. */
+  union value *buffer;         /* I/O buffer. */
+  size_t buffer_pos;           /* Offset of buffer position. */
+  struct ccase c;              /* Current case. */
+};
+
+
+static void io_error (struct fastfile *, const char *, ...) 
+     PRINTF_FORMAT (2, 3);
+static int safe_open (const char *file_name, int flags);
+static int safe_close (int fd);
+static void write_case_to_disk (struct fastfile *, const struct ccase *);
+static void flush_buffer (struct fastfile *);
+
+static void reader_open_file (struct fastfilereader *);
+
+static void seek_and_fill_buffer (struct fastfilereader *);
+static bool fill_buffer (struct fastfilereader *);
+
+
+/* Number of bytes of case allocated in in-memory fastfiles. */
+static size_t case_bytes;
+
+/* Destroys READER. */
+static void fastfilereader_destroy (struct casereader *cr)
+{
+  struct fastfilereader *reader = (struct fastfilereader *) cr;
+  struct fastfile *ff = (struct fastfile *) casereader_get_casefile (cr);
+
+  if (ff->buffer == NULL)
+    ff->buffer = reader->buffer;
+  else
+    free (reader->buffer);
+
+  if (reader->fd != -1)
+    {
+      if (ff->fd == -1)
+       ff->fd = reader->fd;
+      else
+       safe_close (reader->fd);
+    }
+
+  case_destroy (&reader->c);
+
+  free (reader);
+}
+
+
+
+/* Return the case number of the current case */
+static unsigned long
+fastfilereader_cnum (const struct casereader *cr)
+{
+  const struct fastfilereader *ffr = (const struct fastfilereader *) cr;
+  return ffr->case_idx;
+}
+
+
+/* Returns the next case pointed to by FFR and increments
+   FFR's pointer.  Returns NULL if FFR points beyond the last case.
+*/
+static struct ccase *
+fastfilereader_get_next_case (struct casereader *cr)
+{
+  struct fastfile *ff = (struct fastfile *) casereader_get_casefile (cr);
+  struct fastfilereader *ffr = (struct fastfilereader *) cr;
+  struct ccase *read_case = NULL ;
+
+  if ( ffr->case_idx >= ff->case_cnt  ) 
+    return NULL ;
+
+  if (ff->storage == MEMORY )
+    {
+      size_t block_idx = ffr->case_idx / CASES_PER_BLOCK;
+      size_t case_idx = ffr->case_idx % CASES_PER_BLOCK;
+      read_case = &ff->cases[block_idx][case_idx];
+    }
+  else
+    {
+      if (ffr->buffer_pos + ff->value_cnt > ff->buffer_size)
+       {
+         if (!fill_buffer (ffr))
+           return NULL;
+         ffr->buffer_pos = 0;
+       }
+
+      case_from_values (&ffr->c, ffr->buffer + ffr->buffer_pos,
+                       ff->value_cnt);
+      ffr->buffer_pos += ff->value_cnt;
+      
+      read_case = &ffr->c;
+    }
+  ffr->case_idx++;
+
+  return read_case;
+}
+
+/* Creates and returns a casereader for CF.  A casereader can be used to
+   sequentially read the cases in a fastfile. */
+static struct casereader *
+fastfile_get_reader (const struct casefile *cf_)
+{
+  struct casefile *cf = (struct casefile *) cf_;
+  struct fastfilereader *ffr = xzalloc (sizeof *ffr);
+  struct casereader *reader = (struct casereader *) ffr;
+  struct fastfile *ff = (struct fastfile *) cf;
+
+  assert (!cf->being_destroyed);
+
+  /* Flush the buffer to disk if it's not empty. */
+  if (ff->mode == WRITE && ff->storage == DISK)
+    flush_buffer (ff);
+
+  ff->mode = READ;
+
+  casereader_register (cf, reader, &class_reader);
+
+  ffr->case_idx = 0;
+  reader->destructive = 0;
+  ffr->fd = -1;
+  ffr->buffer = NULL;
+  ffr->buffer_pos = 0;
+  case_nullify (&ffr->c);
+
+  if (ff->storage == DISK)
+    reader_open_file (ffr);
+
+  return reader;
+}
+
+/* Returns the number of `union value's in a case for CF. */
+static size_t
+fastfile_get_value_cnt (const struct casefile *cf)
+{
+  const struct fastfile *ff = (const struct fastfile *) cf;
+  return ff->value_cnt;
+}
+
+/* Appends a copy of case C to fastfile CF.  Not valid after any
+   reader for CF has been created.
+   Returns true if successful, false if an I/O error occurred. */
+static bool
+fastfile_append (struct casefile *cf, const struct ccase *c)
+{
+  struct fastfile *ff = (struct fastfile *) cf;
+  assert (ff->mode == WRITE);
+  assert (c != NULL);
+
+  /* Try memory first. */
+  if (ff->storage == MEMORY)
+    {
+      if (case_bytes < get_workspace ())
+       {
+         size_t block_idx = ff->case_cnt / CASES_PER_BLOCK;
+         size_t case_idx = ff->case_cnt % CASES_PER_BLOCK;
+         struct ccase new_case;
+
+         case_bytes += ff->case_acct_size;
+         case_clone (&new_case, c);
+         if (case_idx == 0)
+           {
+             if ((block_idx & (block_idx - 1)) == 0)
+               {
+                 size_t block_cap = block_idx == 0 ? 1 : block_idx * 2;
+                 ff->cases = xnrealloc (ff->cases,
+                                        block_cap, sizeof *ff->cases);
+               }
+
+             ff->cases[block_idx] = xnmalloc (CASES_PER_BLOCK,
+                                              sizeof **ff->cases);
+           }
+
+         case_move (&ff->cases[block_idx][case_idx], &new_case);
+       }
+      else
+       {
+         casefile_to_disk (cf);
+         assert (ff->storage == DISK);
+         write_case_to_disk (ff, c);
+       }
+    }
+  else
+    write_case_to_disk (ff, c);
+
+  ff->case_cnt++;
+  return ff->ok;
+}
+
+
+/* Returns the number of cases in fastfile CF. */
+static unsigned long
+fastfile_get_case_cnt (const struct casefile *cf)
+{
+  const struct fastfile *ff = (const struct fastfile *) cf;
+  return ff->case_cnt;
+}
+
+
+/* Returns true only if fastfile CF is stored in memory (instead of on
+   disk), false otherwise. */
+static bool
+fastfile_in_core (const struct casefile *cf)
+{
+  const struct fastfile *ff = (const struct fastfile *) cf;
+  return (ff->storage == MEMORY);
+}
+
+
+/* If CF is currently stored in memory, writes it to disk.  Readers, if any,
+   retain their current positions.
+   Returns true if successful, false if an I/O error occurred. */
+static bool
+fastfile_to_disk (const struct casefile *cf_)
+{
+  struct fastfile *ff = (struct fastfile *) cf_;
+  struct casefile *cf = &ff->cf;
+
+  if (ff->storage == MEMORY)
+    {
+      size_t idx, block_cnt;
+      struct casereader *reader;
+
+      assert (ff->file_name == NULL);
+      assert (ff->fd == -1);
+      assert (ff->buffer_used == 0);
+
+      if (!make_temp_file (&ff->fd, &ff->file_name))
+       {
+         ff->ok = false;
+         return false;
+       }
+      ff->storage = DISK;
+
+      ff->buffer = xnmalloc (ff->buffer_size, sizeof *ff->buffer);
+      memset (ff->buffer, 0, ff->buffer_size * sizeof *ff->buffer);
+
+      case_bytes -= ff->case_cnt * ff->case_acct_size;
+      for (idx = 0; idx < ff->case_cnt; idx++)
+       {
+         size_t block_idx = idx / CASES_PER_BLOCK;
+         size_t case_idx = idx % CASES_PER_BLOCK;
+         struct ccase *c = &ff->cases[block_idx][case_idx];
+         write_case_to_disk (ff, c);
+         case_destroy (c);
+       }
+
+      block_cnt = DIV_RND_UP (ff->case_cnt, CASES_PER_BLOCK);
+      for (idx = 0; idx < block_cnt; idx++)
+       free (ff->cases[idx]);
+
+      free (ff->cases);
+      ff->cases = NULL;
+
+      if (ff->mode == READ)
+       flush_buffer (ff);
+
+      ll_for_each (reader, struct casereader, ll, &cf->reader_list)
+       reader_open_file ((struct fastfilereader *) reader);
+
+    }
+  return ff->ok;
+}
+
+/* Puts a fastfile to "sleep", that is, minimizes the resources
+   needed for it by closing its file descriptor and freeing its
+   buffer.  This is useful if we need so many fastfiles that we
+   might not have enough memory and file descriptors to go
+   around.
+
+   For simplicity, this implementation always converts the
+   fastfile to reader mode.  If this turns out to be a problem,
+   with a little extra work we could also support sleeping
+   writers.
+
+   Returns true if successful, false if an I/O error occurred. */
+static bool
+fastfile_sleep (const struct casefile *cf_)
+{
+  struct fastfile *ff = (struct fastfile *) cf_;
+  struct casefile *cf = &ff->cf;
+
+  fastfile_to_disk (cf);
+  flush_buffer (ff);
+
+  if (ff->fd != -1)
+    {
+      safe_close (ff->fd);
+      ff->fd = -1;
+    }
+  if (ff->buffer != NULL)
+    {
+      free (ff->buffer);
+      ff->buffer = NULL;
+    }
+
+  return ff->ok;
+}
+
+
+/* Returns true if an I/O error has occurred in fastfile CF. */
+static bool
+fastfile_error (const struct casefile *cf)
+{
+  const struct fastfile *ff = (const struct fastfile *) cf;
+  return !ff->ok;
+}
+
+/* Destroys fastfile CF. */
+static void
+fastfile_destroy (struct casefile *cf)
+{
+  struct fastfile *ff = (struct fastfile *) cf;
+
+  if (cf != NULL)
+    {
+      if (ff->cases != NULL)
+       {
+         size_t idx, block_cnt;
+
+         case_bytes -= ff->case_cnt * ff->case_acct_size;
+         for (idx = 0; idx < ff->case_cnt; idx++)
+           {
+             size_t block_idx = idx / CASES_PER_BLOCK;
+             size_t case_idx = idx % CASES_PER_BLOCK;
+             struct ccase *c = &ff->cases[block_idx][case_idx];
+             case_destroy (c);
+           }
+
+         block_cnt = DIV_RND_UP (ff->case_cnt, CASES_PER_BLOCK);
+         for (idx = 0; idx < block_cnt; idx++)
+           free (ff->cases[idx]);
+
+         free (ff->cases);
+       }
+
+      if (ff->fd != -1)
+       safe_close (ff->fd);
+
+      if (ff->file_name != NULL && remove (ff->file_name) == -1)
+       io_error (ff, _("%s: Removing temporary file: %s."),
+                 ff->file_name, strerror (errno));
+      free (ff->file_name);
+
+      free (ff->buffer);
+
+      free (ff);
+    }
+}
+
+
+/* Creates and returns a fastfile to store cases of VALUE_CNT
+   `union value's each. */
+struct casefile *
+fastfile_create (size_t value_cnt)
+{
+  struct fastfile *ff = xzalloc (sizeof *ff);
+  struct casefile *cf = &ff->cf;
+
+  casefile_register (cf, &class);
+
+  ff->value_cnt = value_cnt;
+  ff->case_acct_size = (ff->value_cnt + 4) * sizeof *ff->buffer;
+  ff->case_cnt = 0;
+  ff->storage = MEMORY;
+  ff->mode = WRITE;
+  cf->being_destroyed = false;
+  ff->ok = true;
+  ff->cases = NULL;
+  ff->fd = -1;
+  ff->file_name = NULL;
+  ff->buffer = NULL;
+  ff->buffer_size = ROUND_UP (ff->value_cnt, IO_BUF_SIZE);
+  if (ff->value_cnt > 0 && ff->buffer_size % ff->value_cnt > 64)
+    ff->buffer_size = ff->value_cnt;
+  ff->buffer_used = 0;
+
+  return cf;
+}
+
+
+
+/* Marks FF as having encountered an I/O error.
+   If this is the first error on CF, reports FORMAT to the user,
+   doing printf()-style substitutions. */
+static void
+io_error (struct fastfile *ff, const char *format, ...)
+{
+  if (ff->ok)
+    {
+      struct msg m;
+      va_list args;
+
+      m.category = MSG_GENERAL;
+      m.severity = MSG_ERROR;
+      m.where.file_name = NULL;
+      m.where.line_number = -1;
+      va_start (args, format);
+      m.text = xvasprintf (format, args);
+      va_end (args);
+
+      msg_emit (&m);
+    }
+  ff->ok = false;
+}
+
+/* Calls open(), passing FILE_NAME and FLAGS, repeating as necessary
+   to deal with interrupted calls. */
+static int
+safe_open (const char *file_name, int flags)
+{
+  int fd;
+
+  do
+    {
+      fd = open (file_name, flags);
+    }
+  while (fd == -1 && errno == EINTR);
+
+  return fd;
+}
+
+/* Calls close(), passing FD, repeating as necessary to deal with
+   interrupted calls. */
+static int
+safe_close (int fd)
+{
+  int retval;
+
+  do
+    {
+      retval = close (fd);
+    }
+  while (retval == -1 && errno == EINTR);
+
+  return retval;
+}
+
+
+/* Writes case C to fastfile CF's disk buffer, first flushing the buffer to
+   disk if it would otherwise overflow.
+   Returns true if successful, false if an I/O error occurred. */
+static void
+write_case_to_disk (struct fastfile *ff, const struct ccase *c)
+{
+  if (!ff->ok)
+    return;
+
+  case_to_values (c, ff->buffer + ff->buffer_used, ff->value_cnt);
+  ff->buffer_used += ff->value_cnt;
+  if (ff->buffer_used + ff->value_cnt > ff->buffer_size)
+    flush_buffer (ff);
+}
+
+
+/* If any bytes in FF's output buffer are used, flush them to
+   disk. */
+static void
+flush_buffer (struct fastfile *ff)
+{
+  if (ff->ok && ff->buffer_used > 0)
+    {
+      if (!full_write (ff->fd, ff->buffer,
+                      ff->buffer_size * sizeof *ff->buffer))
+       io_error (ff, _("Error writing temporary file: %s."),
+                 strerror (errno));
+      ff->buffer_used = 0;
+    }
+}
+
+
+/* Opens a disk file for READER and seeks to the current position as indicated
+   by case_idx.  Normally the current position is the beginning of the file,
+   but fastfile_to_disk may cause the file to be opened at a different
+   position. */
+static void
+reader_open_file (struct fastfilereader *reader)
+{
+  struct casefile *cf = casereader_get_casefile(&reader->cr);
+  struct fastfile *ff = (struct fastfile *) cf;
+  if (!ff->ok || reader->case_idx >= ff->case_cnt)
+    return;
+
+  if (ff->fd != -1)
+    {
+      reader->fd = ff->fd;
+      ff->fd = -1;
+    }
+  else
+    {
+      reader->fd = safe_open (ff->file_name, O_RDONLY);
+      if (reader->fd < 0)
+       io_error (ff, _("%s: Opening temporary file: %s."),
+                 ff->file_name, strerror (errno));
+    }
+
+  if (ff->buffer != NULL)
+    {
+      reader->buffer = ff->buffer;
+      ff->buffer = NULL;
+    }
+  else
+    {
+      reader->buffer = xnmalloc (ff->buffer_size, sizeof *ff->buffer);
+      memset (reader->buffer, 0, ff->buffer_size * sizeof *ff->buffer);
+    }
+
+  case_create (&reader->c, ff->value_cnt);
+
+  reader->buffer_ofs = -1;
+  reader->file_ofs = -1;
+  seek_and_fill_buffer (reader);
+}
+
+/* Seeks the backing file for READER to the proper position and
+   refreshes the buffer contents. */
+static void
+seek_and_fill_buffer (struct fastfilereader *reader)
+{
+  struct casefile *cf = casereader_get_casefile(&reader->cr);
+  struct fastfile *ff = (struct fastfile *) cf;
+  off_t new_ofs;
+
+  if (ff->value_cnt != 0)
+    {
+      size_t buffer_case_cnt = ff->buffer_size / ff->value_cnt;
+      new_ofs = ((off_t) reader->case_idx / buffer_case_cnt
+                * ff->buffer_size * sizeof *ff->buffer);
+      reader->buffer_pos = (reader->case_idx % buffer_case_cnt
+                           * ff->value_cnt);
+    }
+  else
+    new_ofs = 0;
+  if (new_ofs != reader->file_ofs)
+    {
+      if (lseek (reader->fd, new_ofs, SEEK_SET) != new_ofs)
+       io_error (ff, _("%s: Seeking temporary file: %s."),
+                 ff->file_name, strerror (errno));
+      else
+       reader->file_ofs = new_ofs;
+    }
+
+  if (ff->case_cnt > 0 && ff->value_cnt > 0 && reader->buffer_ofs != new_ofs)
+    fill_buffer (reader);
+}
+
+/* Fills READER's buffer by reading a block from disk. */
+static bool
+fill_buffer (struct fastfilereader *reader)
+{
+  struct casefile *cf = casereader_get_casefile(&reader->cr);
+  struct fastfile *ff = (struct fastfile *) cf;
+  if (ff->ok)
+    {
+      int bytes = full_read (reader->fd, reader->buffer,
+                            ff->buffer_size *
+                            sizeof *reader->buffer);
+      if (bytes < 0)
+       io_error (ff, _("%s: Reading temporary file: %s."),
+                 ff->file_name, strerror (errno));
+      else if (bytes != ff->buffer_size * sizeof *reader->buffer)
+       io_error (ff, _("%s: Temporary file ended unexpectedly."),
+                 ff->file_name);
+      else
+       {
+         reader->buffer_ofs = reader->file_ofs;
+         reader->file_ofs += bytes;
+       }
+    }
+  return ff->ok;
+}
+
+static const struct class_casefile class = 
+  {
+    fastfile_destroy,
+    fastfile_error,
+    fastfile_get_value_cnt,
+    fastfile_get_case_cnt,
+    fastfile_get_reader,
+    fastfile_append,
+
+
+    fastfile_in_core,
+    fastfile_to_disk,
+    fastfile_sleep,
+  };
+
+static const struct class_casereader class_reader = 
+  {
+    fastfilereader_get_next_case,
+    fastfilereader_cnum,
+    fastfilereader_destroy,
+  };
diff --git a/src/data/fastfile.h b/src/data/fastfile.h
new file mode 100644 (file)
index 0000000..8404a33
--- /dev/null
@@ -0,0 +1,27 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2004, 2006 Free Software Foundation, Inc.
+   Written by Ben Pfaff <blp@gnu.org>.
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#ifndef FASTFILE_H
+#define FASTFILE_H
+
+#include <config.h>
+
+struct casefile *fastfile_create (size_t value_cnt);
+
+#endif
index 4dcce32b0f8ec8ac467423d52c1d25ebf4cdb951..5649a173a223446a6d27b0d659d39fb8a455889b 100644 (file)
@@ -28,6 +28,7 @@
 #include <data/case-sink.h>
 #include <data/case.h>
 #include <data/casefile.h>
 #include <data/case-sink.h>
 #include <data/case.h>
 #include <data/casefile.h>
+#include <data/fastfile.h>
 #include <data/dictionary.h>
 #include <data/file-handle-def.h>
 #include <data/procedure.h>
 #include <data/dictionary.h>
 #include <data/file-handle-def.h>
 #include <data/procedure.h>
@@ -168,7 +169,7 @@ multipass_procedure (bool (*proc_func) (const struct casefile *, void *aux),
   struct multipass_aux_data aux_data;
   bool ok;
 
   struct multipass_aux_data aux_data;
   bool ok;
 
-  aux_data.casefile = casefile_create (dict_get_next_value_idx (default_dict));
+  aux_data.casefile = fastfile_create (dict_get_next_value_idx (default_dict));
   aux_data.proc_func = proc_func;
   aux_data.aux = aux;
 
   aux_data.proc_func = proc_func;
   aux_data.aux = aux;
 
@@ -618,7 +619,7 @@ multipass_split_case_func (const struct ccase *c, void *aux_)
         ok = multipass_split_output (aux);
 
       /* Start a new casefile. */
         ok = multipass_split_output (aux);
 
       /* Start a new casefile. */
-      aux->casefile = casefile_create (dict_get_next_value_idx (default_dict));
+      aux->casefile = fastfile_create (dict_get_next_value_idx (default_dict));
     }
 
   return casefile_append (aux->casefile, c) && ok;
     }
 
   return casefile_append (aux->casefile, c) && ok;
index 4802f4b20363b5f946660e6c920c10631fbb89b8..13ccee8c6f0aef698c2542ca495dbf4280bf5677 100644 (file)
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include "case.h"
 #include "casefile.h"
 #include <stdlib.h>
 #include "case.h"
 #include "casefile.h"
+#include "fastfile.h"
 #include "dictionary.h"
 #include "file-handle-def.h"
 #include "scratch-handle.h"
 #include "dictionary.h"
 #include "file-handle-def.h"
 #include "scratch-handle.h"
@@ -72,7 +73,7 @@ scratch_writer_open (struct file_handle *fh,
   /* Create new contents. */
   sh = xmalloc (sizeof *sh);
   sh->dictionary = scratch_dict;
   /* Create new contents. */
   sh = xmalloc (sizeof *sh);
   sh->dictionary = scratch_dict;
-  sh->casefile = casefile_create (dict_get_next_value_idx (sh->dictionary));
+  sh->casefile = fastfile_create (dict_get_next_value_idx (sh->dictionary));
 
   /* Create writer. */
   writer = xmalloc (sizeof *writer);
 
   /* Create writer. */
   writer = xmalloc (sizeof *writer);
@@ -115,7 +116,6 @@ scratch_writer_close (struct scratch_writer *writer)
 {
   struct casefile *cf = writer->handle->casefile;
   bool ok = casefile_error (cf);
 {
   struct casefile *cf = writer->handle->casefile;
   bool ok = casefile_error (cf);
-  casefile_mode_reader (cf);
   fh_close (writer->fh, "scratch file", "we");
   free (writer);
   return ok;
   fh_close (writer->fh, "scratch file", "we");
   free (writer);
   return ok;
index 731fea39ba782125c3c61e1652f7a0c19576be42..74769023e74749725183eddb7f6e477a3d1dcca6 100644 (file)
@@ -28,6 +28,7 @@
 #include <data/case-source.h>
 #include <data/case.h>
 #include <data/casefile.h> 
 #include <data/case-source.h>
 #include <data/case.h>
 #include <data/casefile.h> 
+#include <data/fastfile.h> 
 
 #include "xalloc.h"
 
 
 #include "xalloc.h"
 
@@ -46,7 +47,7 @@ storage_sink_open (struct case_sink *sink)
   struct storage_stream_info *info;
 
   sink->aux = info = xmalloc (sizeof *info);
   struct storage_stream_info *info;
 
   sink->aux = info = xmalloc (sizeof *info);
-  info->casefile = casefile_create (sink->value_cnt);
+  info->casefile = fastfile_create (sink->value_cnt);
 }
 
 /* Destroys storage stream represented by INFO. */
 }
 
 /* Destroys storage stream represented by INFO. */
index fc4d2c12911ef39123613286143b59f8c657581b..789d259e6cbb1a3a197788ff92ed64b7d3f439e8 100644 (file)
@@ -27,6 +27,7 @@
 #include <data/case-source.h>
 #include <data/case.h>
 #include <data/casefile.h>
 #include <data/case-source.h>
 #include <data/case.h>
 #include <data/casefile.h>
+#include <data/fastfile.h>
 #include <data/dictionary.h>
 #include <data/por-file-writer.h>
 #include <data/procedure.h>
 #include <data/dictionary.h>
 #include <data/por-file-writer.h>
 #include <data/procedure.h>
@@ -1126,7 +1127,7 @@ cmd_match_files (void)
     discard_variables ();
 
   dict_compact_values (mtf.dict);
     discard_variables ();
 
   dict_compact_values (mtf.dict);
-  mtf.output = casefile_create (dict_get_next_value_idx (mtf.dict));
+  mtf.output = fastfile_create (dict_get_next_value_idx (mtf.dict));
   mtf.seq_nums = xcalloc (dict_get_var_cnt (mtf.dict), sizeof *mtf.seq_nums);
   case_create (&mtf.mtf_case, dict_get_next_value_idx (mtf.dict));
 
   mtf.seq_nums = xcalloc (dict_get_var_cnt (mtf.dict), sizeof *mtf.seq_nums);
   case_create (&mtf.mtf_case, dict_get_next_value_idx (mtf.dict));
 
index 432cf388e944f1d9add1345db209c62f510fe038..a90cbfb7e7a1dc4f5b26d622478e40d8b94ee522 100644 (file)
@@ -19,6 +19,8 @@
 
 #include <config.h>
 #include <data/casefile.h>
 
 #include <config.h>
 #include <data/casefile.h>
+#include <data/fastfile.h>
+
 #include <data/case.h>
 
 #include <gsl/gsl_randist.h>
 #include <data/case.h>
 
 #include <gsl/gsl_randist.h>
@@ -88,7 +90,7 @@ test_casefile (int pattern, size_t value_cnt, size_t case_cnt)
   size_t i, j;
 
   rng = gsl_rng_alloc (gsl_rng_mt19937);
   size_t i, j;
 
   rng = gsl_rng_alloc (gsl_rng_mt19937);
-  cf = casefile_create (value_cnt);
+  cf = fastfile_create (value_cnt);
   if (pattern == 5)
     casefile_to_disk (cf);
   for (i = 0; i < case_cnt; i++)
   if (pattern == 5)
     casefile_to_disk (cf);
   for (i = 0; i < case_cnt; i++)
@@ -136,24 +138,6 @@ test_casefile (int pattern, size_t value_cnt, size_t case_cnt)
     casereader_destroy (r1);
   if (pattern != 2)
     casereader_destroy (r2);
     casereader_destroy (r1);
   if (pattern != 2)
     casereader_destroy (r2);
-  if (pattern > 3) 
-    {
-      int *order;
-      r1 = casefile_get_random_reader (cf);
-      order = xmalloc (sizeof *order * case_cnt);
-      for (i = 0; i < case_cnt; i++)
-        order[i] = i;
-      if (case_cnt > 0)
-        gsl_ran_shuffle (rng, order, case_cnt, sizeof *order);
-      for (i = 0; i < case_cnt; i++)
-        {
-          int case_idx = order[i];
-          casereader_seek (r1, case_idx);
-          read_and_verify_random_case (cf, r1, case_idx);
-        }
-      casereader_destroy (r1);
-      free (order);
-    }
   if (pattern > 2) 
     {
       r1 = casefile_get_destructive_reader (cf);
   if (pattern > 2) 
     {
       r1 = casefile_get_destructive_reader (cf);
index 3ce5da5707cb2ab1d3508eadf3c48a7ad5b6f0a2..732c4b86555d30d7362bb0ac85d9199282235453 100644 (file)
@@ -30,6 +30,7 @@
 #include <data/case-source.h>
 #include <data/case.h>
 #include <data/casefile.h>
 #include <data/case-source.h>
 #include <data/case.h>
 #include <data/casefile.h>
+#include <data/fastfile.h>
 #include <data/procedure.h>
 #include <data/settings.h>
 #include <data/variable.h>
 #include <data/procedure.h>
 #include <data/settings.h>
 #include <data/variable.h>
@@ -162,7 +163,7 @@ do_internal_sort (struct casereader *reader,
     return NULL;
       
   case_cnt = casefile_get_case_cnt (src);
     return NULL;
       
   case_cnt = casefile_get_case_cnt (src);
-  dst = casefile_create (casefile_get_value_cnt (src));
+  dst = fastfile_create (casefile_get_value_cnt (src));
   if (case_cnt != 0) 
     {
       struct indexed_case *cases = nmalloc (sizeof *cases, case_cnt);
   if (case_cnt != 0) 
     {
       struct indexed_case *cases = nmalloc (sizeof *cases, case_cnt);
@@ -508,7 +509,7 @@ start_run (struct initial_run_state *irs)
 {
   irs->run++;
   irs->case_cnt = 0;
 {
   irs->run++;
   irs->case_cnt = 0;
-  irs->casefile = casefile_create (irs->xsrt->value_cnt);
+  irs->casefile = fastfile_create (irs->xsrt->value_cnt);
   casefile_to_disk (irs->casefile);
   case_nullify (&irs->last_output); 
 }
   casefile_to_disk (irs->casefile);
   case_nullify (&irs->last_output); 
 }
@@ -673,7 +674,7 @@ merge_once (struct external_sort *xsrt,
     }
 
   /* Create output file. */
     }
 
   /* Create output file. */
-  output = casefile_create (xsrt->value_cnt);
+  output = fastfile_create (xsrt->value_cnt);
   casefile_to_disk (output);
 
   /* Merge. */
   casefile_to_disk (output);
 
   /* Merge. */
index 4a96aeb941a9479876c7386203e97d30fc102f8b..f4a3751332c7930a8579c457a91203222a13c22a 100644 (file)
@@ -1,3 +1,7 @@
+Mon Jul 17 18:22:18 WST 2006 John Darrington <john@darrington.wattle.id.au>
+
+       * flexifile.c flexifile.h: New files. Implementations of casefiles.
+
 Thu Mar  2 08:40:33 WST 2006 John Darrington <john@darrington.wattle.id.au>
        
        * Moved files from src directory
 Thu Mar  2 08:40:33 WST 2006 John Darrington <john@darrington.wattle.id.au>
        
        * Moved files from src directory
diff --git a/src/ui/automake.mk b/src/ui/automake.mk
new file mode 100644 (file)
index 0000000..5bf7862
--- /dev/null
@@ -0,0 +1,13 @@
+## Process this file with automake to produce Makefile.in  -*- makefile -*-
+
+include $(top_srcdir)/src/ui/terminal/automake.mk
+if WITHGUI
+include $(top_srcdir)/src/ui/gui/automake.mk
+endif
+
+
+noinst_LIBRARIES += src/ui/libuicommon.a
+
+src_ui_libuicommon_a_SOURCES = \
+       src/ui/flexifile.c \
+       src/ui/flexifile.h
diff --git a/src/ui/flexifile.c b/src/ui/flexifile.c
new file mode 100644 (file)
index 0000000..756f67a
--- /dev/null
@@ -0,0 +1,379 @@
+/* PSPP - computes sample statistics.
+
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#include <config.h>
+#include <xalloc.h>
+#include <assert.h>
+#include "flexifile.h"
+#include <data/casefile.h>
+#include <data/casefile-private.h>
+#include <data/case.h>
+
+
+struct class_flexifile
+{
+  struct class_casefile parent;
+
+  bool (*get_case) (const struct flexifile *, unsigned long, struct ccase *);
+
+  bool (*insert_case) (struct flexifile *, struct ccase *, int );
+  bool (*delete_cases) (struct flexifile *, int, int );
+
+  bool (*resize) (struct flexifile *, int, int );
+};
+
+static const struct class_flexifile class;
+
+#define CLASS_FLEXIFILE(K)  ((struct class_flexifile *) K)
+#define CONST_CLASS_FLEXIFILE(K) ((const struct class_flexifile *) K)
+
+
+/* A flexifile. */
+struct flexifile
+{
+  struct casefile cf;          /* Parent */
+
+  size_t value_cnt;            /* Case size in `union value's. */
+  unsigned long case_cnt;      /* Number of cases stored. */
+
+
+  /* Memory storage. */
+  struct ccase *cases;         /* Pointer to array of cases. */
+  unsigned long capacity;       /* size of array in cases */
+};
+
+struct class_flexifilereader 
+{
+  struct class_casereader parent ;
+};
+
+static const struct class_flexifilereader class_reader;
+
+/* For reading out the cases in a flexifile. */
+struct flexifilereader
+{
+  struct casereader cr;                /* Parent */
+
+  unsigned long case_idx;      /* Case number of current case. */
+  bool destructive;            /* Is this a destructive reader? */
+};
+
+
+
+#define CHUNK_SIZE 10
+
+static bool 
+impl_get_case(const struct flexifile *ff, unsigned long casenum, 
+             struct ccase *);
+static bool
+impl_insert_case (struct flexifile *ff, struct ccase *c, int posn);
+
+static bool 
+impl_delete_cases (struct flexifile *ff, int n_cases, int first);
+
+static bool 
+impl_resize (struct flexifile *ff, int n_values, int posn);
+
+
+/* Gets a case, for which writing may not be safe */
+bool 
+flexifile_get_case(const struct flexifile *ff, unsigned long casenum, 
+                  struct ccase *c)
+{
+  const struct class_flexifile *class = 
+    CONST_CLASS_FLEXIFILE (CONST_CASEFILE(ff)->class) ;
+
+  return class->get_case(ff, casenum, c);
+}
+
+
+/* Insert N_VALUES before POSN.
+   If N_VALUES is negative, then deleted -N_VALUES instead
+*/
+bool
+flexifile_resize (struct flexifile *ff, int n_values, int posn)
+{
+  const struct class_flexifile *class = 
+    CONST_CLASS_FLEXIFILE (CONST_CASEFILE(ff)->class) ;
+
+  return class->resize(ff, n_values, posn);
+}
+
+
+
+bool
+flexifile_insert_case (struct flexifile *ff, struct ccase *c, int posn)
+{
+  const struct class_flexifile *class = 
+    CONST_CLASS_FLEXIFILE (CONST_CASEFILE(ff)->class) ;
+
+  return class->insert_case(ff, c, posn);
+}
+
+
+bool
+flexifile_delete_cases (struct flexifile *ff, int n_cases, int first)
+{
+  const struct class_flexifile *class = 
+    CONST_CLASS_FLEXIFILE (CONST_CASEFILE(ff)->class) ;
+
+  return class->delete_cases (ff, n_cases, first);
+}
+
+
+static unsigned long 
+flexifile_get_case_cnt (const struct casefile *cf)
+{
+  return FLEXIFILE(cf)->case_cnt;
+}
+
+static size_t
+flexifile_get_value_cnt (const struct casefile *cf)
+{
+  return FLEXIFILE(cf)->value_cnt;
+}
+
+
+static void
+flexifile_destroy (struct casefile *cf)
+{
+  int i ; 
+  for ( i = 0 ; i < FLEXIFILE(cf)->case_cnt; ++i ) 
+    case_destroy( &FLEXIFILE(cf)->cases[i]);
+
+  free(FLEXIFILE(cf)->cases);
+}
+
+static void
+grow(struct flexifile *ff) 
+{
+  ff->capacity += CHUNK_SIZE;
+  ff->cases = xrealloc(ff->cases, ff->capacity * sizeof ( *ff->cases) );
+}
+
+static bool
+flexifile_append (struct casefile *cf, const struct ccase *c)
+{
+  struct flexifile *ff =  FLEXIFILE(cf);
+
+  if (ff->case_cnt >= ff->capacity)
+    grow(ff);
+
+  case_clone (&ff->cases[ff->case_cnt++], c);
+
+  return true;
+}
+
+
+static struct ccase *
+flexifilereader_get_next_case (struct casereader *cr)
+{
+  struct flexifilereader *ffr = FLEXIFILEREADER(cr);
+  struct flexifile *ff = FLEXIFILE(casereader_get_casefile(cr));
+
+  if ( ffr->case_idx >= ff->case_cnt) 
+    return NULL;
+
+  return &ff->cases[ffr->case_idx++];
+}
+
+static void
+flexifilereader_destroy(struct casereader *r)
+{
+  free(r);
+}
+
+static struct casereader * 
+flexifile_get_reader (const struct casefile *cf_)
+{
+  struct casefile *cf = (struct casefile *) cf_;
+  struct flexifilereader *ffr = xzalloc (sizeof *ffr);
+  struct casereader *reader = (struct casereader *) ffr;
+
+  casereader_register (cf, reader, CLASS_CASEREADER(&class_reader));
+
+  return reader;
+}
+
+static bool
+flexifile_in_core(const struct casefile *cf UNUSED)
+{
+  /* Always in memory */
+  return true;
+}
+
+static bool
+flexifile_error (const struct casefile *cf UNUSED )
+{
+  return false;
+}
+
+
+struct casefile *
+flexifile_create (size_t value_cnt)
+{
+  struct flexifile *ff = xzalloc (sizeof *ff);
+  struct casefile *cf = (struct casefile *) ff;
+
+  casefile_register (cf, (struct class_casefile *) &class);
+
+  ff->value_cnt = value_cnt;
+  ff->cases = xzalloc(sizeof (struct ccase *) * CHUNK_SIZE);
+  ff->capacity = CHUNK_SIZE;
+  return cf;
+}
+
+static const struct class_flexifile class = {
+  {
+    flexifile_destroy,
+    flexifile_error,
+    flexifile_get_value_cnt,
+    flexifile_get_case_cnt,
+    flexifile_get_reader,
+    flexifile_append,
+
+    flexifile_in_core,
+    0, /* to_disk */
+    0 /* sleep */
+  },
+
+  impl_get_case ,
+  impl_insert_case ,
+  impl_delete_cases,
+  impl_resize,
+};
+
+
+static const struct class_flexifilereader class_reader = 
+  {
+    {
+      flexifilereader_get_next_case,
+      0,  /* cnum */
+      flexifilereader_destroy
+    }
+  };
+
+
+/* Implementations of class methods */
+
+static bool 
+impl_get_case(const struct flexifile *ff, unsigned long casenum, 
+             struct ccase *c)
+{
+  if ( casenum >= ff->case_cnt) 
+    return false;
+
+  case_clone (c, &ff->cases[casenum]);
+  
+  return true;
+}
+
+#if DEBUGGING
+static void
+dumpcasedata(struct ccase *c)
+{
+  int i;
+  for ( i = 0 ; i < c->case_data->value_cnt * MAX_SHORT_STRING; ++i ) 
+    putchar(c->case_data->values->s[i]);
+  putchar('\n');
+}
+#endif
+
+static bool 
+impl_resize (struct flexifile *ff, int n_values, int posn)
+{
+  int i;
+
+  for( i = 0 ; i < ff->case_cnt ; ++i ) 
+    {
+      struct ccase c;
+      case_create (&c, ff->value_cnt + n_values);
+
+      case_copy (&c, 0, &ff->cases[i], 0, posn);
+      if ( n_values > 0 ) 
+       memset (case_data_rw(&c, posn), ' ', n_values * MAX_SHORT_STRING) ;
+      case_copy (&c, posn + n_values, 
+                &ff->cases[i], posn, ff->value_cnt - posn);
+
+      case_destroy (&ff->cases[i]);
+      ff->cases[i] = c;
+    }
+
+  ff->value_cnt += n_values;
+
+  return true;
+}
+
+static bool
+impl_insert_case (struct flexifile *ff, struct ccase *c, int posn)
+{
+  int i;
+  struct ccase blank;
+  
+  assert (ff);
+
+  if ( posn > ff->case_cnt )
+    return false;
+
+  if ( posn >= ff->capacity ) 
+    grow(ff);
+
+  case_create(&blank, ff->value_cnt);
+
+  flexifile_append(CASEFILE(ff), &blank);
+
+  case_destroy(&blank);
+
+  /* Shift the existing cases down one */
+  for ( i = ff->case_cnt ; i > posn; --i)
+      case_move(&ff->cases[i], &ff->cases[i-1]);
+
+  case_clone (&ff->cases[posn], c);
+
+  return true;
+}
+
+
+static bool 
+impl_delete_cases (struct flexifile *ff, int n_cases, int first)
+{
+  int i;
+
+  if ( ff->case_cnt < first + n_cases ) 
+    return false;
+
+  for ( i = first ; i < first + n_cases; ++i ) 
+    case_destroy (&ff->cases[i]);
+  
+  /* Shift the cases up by N_CASES */
+  for ( i = first; i < ff->case_cnt - n_cases; ++i ) 
+    {
+      case_move (&ff->cases[i], &ff->cases[i+ n_cases]);
+    }
+
+  ff->case_cnt -= n_cases;
+  
+  return true;
+}
+
+
+
diff --git a/src/ui/flexifile.h b/src/ui/flexifile.h
new file mode 100644 (file)
index 0000000..673c7d5
--- /dev/null
@@ -0,0 +1,47 @@
+/* PSPP - computes sample statistics.
+   Copyright (C) 2006 Free Software Foundation, Inc.
+   Written by John Darrington <john@darrington.wattle.id.au>
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful, but
+   WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+   02110-1301, USA. */
+
+#ifndef FLEXIFILE_H
+#define FLEXIFILE_H
+
+#include <config.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+struct ccase;
+struct casefile;
+struct casereader;
+struct flexifile;
+struct flexifilereader;
+
+#define FLEXIFILE(CF) ( (struct flexifile *) CF)
+#define FLEXIFILEREADER(CR) ( (struct flexifilereader *) CR)
+
+struct casefile *flexifile_create (size_t value_cnt);
+
+bool flexifile_get_case(const struct flexifile *ff, unsigned long casenum, 
+                       struct ccase *const c);
+
+bool flexifile_resize (struct flexifile *ff, int n_values, int posn);
+
+bool flexifile_insert_case (struct flexifile *ff, struct ccase *c, int posn);
+bool flexifile_delete_cases (struct flexifile *ff, int n_cases, int first);
+
+
+#endif
index e2555263cfcf552c77a2e72ccf1fede3a8e623a2..d24ce2883adcadbd8dd45e80f95c7e2b9c2b2c1c 100644 (file)
@@ -1,3 +1,9 @@
+Mon Jul 17 18:21:29 WST 2006 John Darrington <john@darrington.wattle.id.au>
+
+    * automake.mk menu-actions.c psppire-case-file.c psppire-case-file.h
+    psppire-data-store.c psppire-dict.c psppire-dict.h psppire-variable.c
+    psppire.c psppire.glade: Adjusted code to use the new flexifile object.
+
 Sat Jul 15 11:27:15 WST 2006 John Darrington <john@darrington.wattle.id.au>
 
    * psppire.c psppire.glade automake.mk icons/* : Added toolbar icons where 
 Sat Jul 15 11:27:15 WST 2006 John Darrington <john@darrington.wattle.id.au>
 
    * psppire.c psppire.glade automake.mk icons/* : Added toolbar icons where 
index ca6c8fc97a6927e101a5a9422e3327d71fc12021..be59fed55165d323656e08978b3e77e7b84a9f6f 100644 (file)
@@ -11,6 +11,7 @@ src_ui_gui_psppire_LDADD = \
        $(GTK_LIBS) \
        $(GLADE_LIBS) \
        $(top_builddir)/lib/gtksheet/libgtksheet.a \
        $(GTK_LIBS) \
        $(GLADE_LIBS) \
        $(top_builddir)/lib/gtksheet/libgtksheet.a \
+       $(top_builddir)/src/ui/libuicommon.a \
        $(top_builddir)/src/math/libpspp_math.a \
        $(top_builddir)/src/data/libdata.a \
        $(top_builddir)/src/libpspp/libpspp.a \
        $(top_builddir)/src/math/libpspp_math.a \
        $(top_builddir)/src/data/libdata.a \
        $(top_builddir)/src/libpspp/libpspp.a \
index d1a76802840b8970b84f57b9fc1e596771853e9f..98d00f63af619c94c5c73504a024b3dce976ce6b 100644 (file)
@@ -196,9 +196,7 @@ load_system_file(const gchar *file_name)
          g_warning("Cannot write case to casefile\n");
          break;
        }
          g_warning("Cannot write case to casefile\n");
          break;
        }
-
       case_destroy(&c);
       case_destroy(&c);
-
     }
   
   sfm_close_reader(reader);      
     }
   
   sfm_close_reader(reader);      
@@ -345,58 +343,61 @@ on_quit1_activate                      (GtkMenuItem     *menuitem,
 
 
 void
 
 
 void
-on_cut1_activate                       (GtkMenuItem     *menuitem,
-                                        gpointer         user_data)
-{
-
-}
-
-
-void
-on_copy1_activate                      (GtkMenuItem     *menuitem,
-                                        gpointer         user_data)
+on_clear_activate                    (GtkMenuItem     *menuitem,
+                                     gpointer         user_data)
 {
 {
+  GtkNotebook *notebook = GTK_NOTEBOOK(get_widget_assert(xml, "notebook1"));
+  gint page = -1;
 
 
-}
-
-
-void
-on_paste1_activate                     (GtkMenuItem     *menuitem,
-                                        gpointer         user_data)
-{
-
-}
-
-/* Fill a case with SYSMIS for numeric and whitespace for string
-   variables respectively */ 
-static gboolean 
-blank_case(struct ccase *cc, gpointer _dict)
-{
-  gint i;
-  PsppireDict *dict = _dict;
+  page = gtk_notebook_get_current_page(notebook);
 
 
-  for(i = 0 ; i < psppire_dict_get_var_cnt(dict); ++i 
+  switch (page
     {
     {
-      union value *val ;
-
-      const struct PsppireVariable *var = psppire_dict_get_variable(dict, i);
-      
-      gint idx = psppire_variable_get_fv(var);
-
-      val = case_data_rw(cc, idx) ;
+    case PAGE_DATA_SHEET:
+      {
+       GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
+       PsppireDataStore *data_store = 
+         PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
+       
 
 
-      if ( psppire_variable_get_type(var) == ALPHA ) 
-       memset(val->s, ' ', psppire_variable_get_width(var));
-      else
-       val->f = SYSMIS;
+       switch ( data_sheet->state ) 
+         {
+         case GTK_SHEET_ROW_SELECTED:
+           psppire_case_file_delete_cases(data_store->case_file,
+                                          data_sheet->range.rowi 
+                                          - data_sheet->range.row0 + 1,
+                                          data_sheet->range.row0);
+           break;
+         case GTK_SHEET_COLUMN_SELECTED:
+           {
+             gint fv;
+             struct PsppireVariable *pv = 
+               psppire_dict_get_variable(the_dictionary, 
+                                         data_sheet->range.col0);
+             fv = psppire_variable_get_fv(pv);
+             
+             
+             psppire_dict_delete_variables(the_dictionary, 
+                                           data_sheet->range.col0,
+                                           1);
+
+             psppire_case_file_insert_values(data_store->case_file, 
+                                             -1, fv);
+           }
+           break;
+         default:
+           gtk_sheet_cell_clear(data_sheet, 
+                                data_sheet->active_cell.row,
+                                data_sheet->active_cell.col);
+           break;
+         }
 
 
-      case_unshare(cc);
+      }
+      break;
     }
 
     }
 
-  return TRUE;
 }
 
 }
 
-
 void
 on_insert1_activate                    (GtkMenuItem     *menuitem,
                                         gpointer         user_data)
 void
 on_insert1_activate                    (GtkMenuItem     *menuitem,
                                         gpointer         user_data)
@@ -435,6 +436,7 @@ on_insert1_activate                    (GtkMenuItem     *menuitem,
     }
 }
 
     }
 }
 
+#if 0
 void
 on_delete1_activate                    (GtkMenuItem     *menuitem,
                                         gpointer         user_data)
 void
 on_delete1_activate                    (GtkMenuItem     *menuitem,
                                         gpointer         user_data)
@@ -475,7 +477,7 @@ on_delete1_activate                    (GtkMenuItem     *menuitem,
       break;
     }
 }
       break;
     }
 }
-
+#endif
 
 void
 on_about1_activate(GtkMenuItem     *menuitem,
 
 void
 on_about1_activate(GtkMenuItem     *menuitem,
@@ -601,15 +603,22 @@ static GtkNotebook *notebook = 0;
 static void
 switch_menus(gint page)
 {
 static void
 switch_menus(gint page)
 {
+  GtkWidget *insert_variable = get_widget_assert(xml, "insert-variable");
+  GtkWidget *insert_cases = get_widget_assert(xml, "insert-cases");
+
   switch (page) 
     {
     case PAGE_VAR_SHEET:
       gtk_widget_hide(menuitems[PAGE_VAR_SHEET]);
       gtk_widget_show(menuitems[PAGE_DATA_SHEET]);
   switch (page) 
     {
     case PAGE_VAR_SHEET:
       gtk_widget_hide(menuitems[PAGE_VAR_SHEET]);
       gtk_widget_show(menuitems[PAGE_DATA_SHEET]);
+      gtk_widget_set_sensitive(insert_variable, TRUE);
+      gtk_widget_set_sensitive(insert_cases, FALSE);
       break;
     case PAGE_DATA_SHEET:
       gtk_widget_show(menuitems[PAGE_VAR_SHEET]);
       gtk_widget_hide(menuitems[PAGE_DATA_SHEET]);
       break;
     case PAGE_DATA_SHEET:
       gtk_widget_show(menuitems[PAGE_VAR_SHEET]);
       gtk_widget_hide(menuitems[PAGE_DATA_SHEET]);
+      gtk_widget_set_sensitive(insert_variable, FALSE);
+      gtk_widget_set_sensitive(insert_cases, TRUE);
       break;
     default:
       g_assert_not_reached();
       break;
     default:
       g_assert_not_reached();
@@ -736,3 +745,44 @@ on_sort_cases_activate (GtkMenuItem     *menuitem,
       break;
     }
 }
       break;
     }
 }
+
+
+static void 
+insert_case(void)
+{
+  gint row, col;
+  PsppireDataStore *data_store ;
+  GtkSheet *data_sheet = GTK_SHEET(get_widget_assert(xml, "data_sheet"));
+
+  data_store = PSPPIRE_DATA_STORE(gtk_sheet_get_model(data_sheet));
+
+  gtk_sheet_get_active_cell(data_sheet, &row, &col);
+
+  psppire_case_file_insert_case(data_store->case_file, row);
+}
+
+void
+on_insert_case_clicked (GtkButton *button, gpointer user_data)      
+{
+  insert_case();
+}
+
+void
+on_insert_cases (GtkMenuItem *menuitem, gpointer user_data)
+{
+  insert_case();
+}
+
+
+void
+on_insert_variable (GtkMenuItem *menuitem, gpointer user_data)
+{
+  gint row, col;
+  GtkSheet *var_sheet = GTK_SHEET(get_widget_assert(xml, "variable_sheet"));
+
+  gtk_sheet_get_active_cell(var_sheet, &row, &col);
+
+  psppire_dict_insert_variable(the_dictionary, row, NULL);
+}
+
+
index 0c8e52a93968430186eed573c37ffff0f46a8b0a..f869e4b0dbf6aaea22383e7735efa8bd56c49397 100644 (file)
 #include <gtksheet/gtkextra-marshal.h>
 
 #include <data/case.h>
 #include <gtksheet/gtkextra-marshal.h>
 
 #include <data/case.h>
+#include <ui/flexifile.h>
 #include <data/casefile.h>
 #include <data/data-in.h>
 #include <data/casefile.h>
 #include <data/data-in.h>
-
 #include <math/sort.h>
 #include <math/sort.h>
+#include <libpspp/misc.h>
 
 /* --- prototypes --- */
 static void psppire_case_file_class_init       (PsppireCaseFileClass   *class);
 
 /* --- prototypes --- */
 static void psppire_case_file_class_init       (PsppireCaseFileClass   *class);
@@ -132,8 +133,8 @@ psppire_case_file_finalize (GObject *object)
 {
   PsppireCaseFile *cf = PSPPIRE_CASE_FILE (object);
   
 {
   PsppireCaseFile *cf = PSPPIRE_CASE_FILE (object);
   
-  if ( cf->casefile) 
-    casefile_destroy(cf->casefile);
+  if ( cf->flexifile) 
+    casefile_destroy(cf->flexifile);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -141,7 +142,7 @@ psppire_case_file_finalize (GObject *object)
 static void
 psppire_case_file_init (PsppireCaseFile *cf)
 {
 static void
 psppire_case_file_init (PsppireCaseFile *cf)
 {
-  cf->casefile = 0;
+  cf->flexifile = 0;
 }
 
 /**
 }
 
 /**
@@ -151,16 +152,55 @@ psppire_case_file_init (PsppireCaseFile *cf)
  * Creates a new #PsppireCaseFile. 
  */
 PsppireCaseFile*
  * Creates a new #PsppireCaseFile. 
  */
 PsppireCaseFile*
-psppire_case_file_new (gint var_cnt)
+psppire_case_file_new (gint val_cnt)
 {
   PsppireCaseFile *cf = g_object_new (G_TYPE_PSPPIRE_CASE_FILE, NULL);
 
 {
   PsppireCaseFile *cf = g_object_new (G_TYPE_PSPPIRE_CASE_FILE, NULL);
 
-  cf->casefile = casefile_create(var_cnt);
+  cf->flexifile = flexifile_create(val_cnt);
 
   return cf;
 }
 
 
 
   return cf;
 }
 
 
+gboolean
+psppire_case_file_delete_cases(PsppireCaseFile *cf, gint n_cases, gint first)
+{
+  int result;
+
+  g_return_val_if_fail(cf, FALSE);
+  g_return_val_if_fail(cf->flexifile, FALSE);
+
+  result =  flexifile_delete_cases(FLEXIFILE(cf->flexifile), n_cases,  first);
+
+  g_signal_emit(cf, signal[CASES_DELETED], 0, n_cases, first);
+
+  return result;
+}
+
+/* Insert a blank case to the case file */
+gboolean
+psppire_case_file_insert_case(PsppireCaseFile *cf, 
+                              gint posn)
+{
+  bool result ;
+  struct ccase cc;
+
+  g_return_val_if_fail(cf, FALSE);
+  g_return_val_if_fail(cf->flexifile, FALSE);
+
+  case_create (&cc, casefile_get_value_cnt(cf->flexifile));
+
+  result = flexifile_insert_case(FLEXIFILE(cf->flexifile), &cc, posn);
+  
+  case_destroy (&cc);
+
+  if ( result ) 
+    g_signal_emit(cf, signal[CASE_INSERTED], 0, posn);
+  else
+    g_warning("Cannot insert case at position %d\n", posn);
+               
+  return result;
+}
 
 /* Append a case to the case file */
 gboolean
 
 /* Append a case to the case file */
 gboolean
@@ -171,11 +211,11 @@ psppire_case_file_append_case(PsppireCaseFile *cf,
   gint posn ;
 
   g_return_val_if_fail(cf, FALSE);
   gint posn ;
 
   g_return_val_if_fail(cf, FALSE);
-  g_return_val_if_fail(cf->casefile, FALSE);
+  g_return_val_if_fail(cf->flexifile, FALSE);
 
 
-  posn = casefile_get_case_cnt(cf->casefile);
+  posn = casefile_get_case_cnt(cf->flexifile);
 
 
-  result = casefile_append(cf->casefile, c);
+  result = casefile_append(cf->flexifile, c);
   
   g_signal_emit(cf, signal[CASE_INSERTED], 0, posn);
                
   
   g_signal_emit(cf, signal[CASE_INSERTED], 0, posn);
                
@@ -188,10 +228,10 @@ psppire_case_file_get_case_count(const PsppireCaseFile *cf)
 {
   g_return_val_if_fail(cf, FALSE);
   
 {
   g_return_val_if_fail(cf, FALSE);
   
-  if ( ! cf->casefile) 
+  if ( ! cf->flexifile) 
     return 0;
 
     return 0;
 
-  return casefile_get_case_cnt(cf->casefile);
+  return casefile_get_case_cnt(cf->flexifile);
 }
 
 /* Return the IDXth value from case CASENUM.
 }
 
 /* Return the IDXth value from case CASENUM.
@@ -202,14 +242,16 @@ psppire_case_file_get_value(const PsppireCaseFile *cf, gint casenum, gint idx)
 {
   const union value *v; 
   struct ccase c;
 {
   const union value *v; 
   struct ccase c;
-  struct casereader *reader =  casefile_get_random_reader (cf->casefile);
 
 
-  casereader_seek(reader, casenum);
+  g_return_val_if_fail(cf, NULL);
+  g_return_val_if_fail(cf->flexifile, NULL);
+
+  g_return_val_if_fail(idx < casefile_get_value_cnt(cf->flexifile), NULL);
 
 
-  casereader_read(reader, &c);
+  flexifile_get_case(FLEXIFILE(cf->flexifile), casenum, &c);
 
   v = case_data(&c, idx);
 
   v = case_data(&c, idx);
-  casereader_destroy(reader);
+
   case_destroy(&c);
 
   return v;
   case_destroy(&c);
 
   return v;
@@ -218,22 +260,56 @@ psppire_case_file_get_value(const PsppireCaseFile *cf, gint casenum, gint idx)
 void
 psppire_case_file_clear(PsppireCaseFile *cf)
 {
 void
 psppire_case_file_clear(PsppireCaseFile *cf)
 {
-  casefile_destroy(cf->casefile);
-  cf->casefile = 0;
+  casefile_destroy(cf->flexifile);
+  cf->flexifile = 0;
   g_signal_emit(cf, signal[CASES_DELETED], 0, 0, -1);
 }
 
   g_signal_emit(cf, signal[CASES_DELETED], 0, 0, -1);
 }
 
-/* Set the IDXth value of case C using FF and DATA */
+/* Set the IDXth value of case C to SYSMIS/EMPTY */
 gboolean
 psppire_case_file_set_value(PsppireCaseFile *cf, gint casenum, gint idx,
 gboolean
 psppire_case_file_set_value(PsppireCaseFile *cf, gint casenum, gint idx,
+                           union value *v, gint width)
+{
+  struct ccase cc ;
+  int bytes;
+
+  g_return_val_if_fail(cf, FALSE);
+  g_return_val_if_fail(cf->flexifile, FALSE);
+
+  g_return_val_if_fail(idx < casefile_get_value_cnt(cf->flexifile), FALSE);
+
+  if ( ! flexifile_get_case(FLEXIFILE(cf->flexifile), casenum, &cc) )
+    return FALSE;
+
+  if ( width == 0 ) 
+    bytes = MAX_SHORT_STRING;
+  else
+    bytes = DIV_RND_UP(width, MAX_SHORT_STRING) * MAX_SHORT_STRING ;
+
+  /* Cast away const in flagrant abuse of the casefile */
+  memcpy((union value *)case_data(&cc, idx), v, bytes);
+
+  g_signal_emit(cf, signal[CASE_CHANGED], 0, casenum);
+
+  return TRUE;
+}
+
+
+
+/* Set the IDXth value of case C using D_IN */
+gboolean
+psppire_case_file_data_in(PsppireCaseFile *cf, gint casenum, gint idx,
                            struct data_in *d_in)
 {
   struct ccase cc ;
 
                            struct data_in *d_in)
 {
   struct ccase cc ;
 
-  struct casereader *reader =  casefile_get_random_reader (cf->casefile);
+  g_return_val_if_fail(cf, FALSE);
+  g_return_val_if_fail(cf->flexifile, FALSE);
+
+  g_return_val_if_fail(idx < casefile_get_value_cnt(cf->flexifile), FALSE);
 
 
-  casereader_seek(reader, casenum);
-  casereader_read(reader, &cc);
+  if ( ! flexifile_get_case(FLEXIFILE(cf->flexifile), casenum, &cc) )
+    return FALSE;
 
   /* Cast away const in flagrant abuse of the casefile */
   d_in->v = (union value *) case_data(&cc, idx);
 
   /* Cast away const in flagrant abuse of the casefile */
   d_in->v = (union value *) case_data(&cc, idx);
@@ -241,9 +317,6 @@ psppire_case_file_set_value(PsppireCaseFile *cf, gint casenum, gint idx,
   if ( ! data_in(d_in) ) 
     g_warning("Cant set value\n");
 
   if ( ! data_in(d_in) ) 
     g_warning("Cant set value\n");
 
-  case_destroy(&cc);
-  casereader_destroy(reader);
-
   g_signal_emit(cf, signal[CASE_CHANGED], 0, casenum);
 
   return TRUE;
   g_signal_emit(cf, signal[CASE_CHANGED], 0, casenum);
 
   return TRUE;
@@ -253,12 +326,45 @@ psppire_case_file_set_value(PsppireCaseFile *cf, gint casenum, gint idx,
 void
 psppire_case_file_sort(PsppireCaseFile *cf, const struct sort_criteria *sc)
 {
 void
 psppire_case_file_sort(PsppireCaseFile *cf, const struct sort_criteria *sc)
 {
+  struct ccase cc;
   gint c;
   gint c;
-  struct casereader *reader = casefile_get_reader(cf->casefile);
-  cf->casefile = sort_execute(reader, sc);
+  struct casefile *cfile;
+  struct casereader *reader = casefile_get_reader(cf->flexifile);
+  const int value_cnt = casefile_get_value_cnt(cf->flexifile);
+
+  cfile = sort_execute(reader, sc);
+
+  casefile_destroy(cf->flexifile);
+
+  /* Copy casefile into flexifile */
+
+  reader = casefile_get_destructive_reader(cfile);
+  cf->flexifile = flexifile_create(value_cnt);
+  while(casereader_read(reader, &cc))
+      casefile_append(cf->flexifile, &cc);
+  
 
   /* FIXME: Need to have a signal to change a range of cases, instead of
      calling a signal many times */
 
   /* FIXME: Need to have a signal to change a range of cases, instead of
      calling a signal many times */
-  for ( c = 0 ; c < casefile_get_case_cnt(cf->casefile) ; ++c ) 
+  for ( c = 0 ; c < casefile_get_case_cnt(cf->flexifile) ; ++c ) 
     g_signal_emit(cf, signal[CASE_CHANGED], 0, c);
 }
     g_signal_emit(cf, signal[CASE_CHANGED], 0, c);
 }
+
+
+/* Resize the cases in the casefile, by inserting N_VALUES into every 
+   one of them. */
+gboolean 
+psppire_case_file_insert_values(PsppireCaseFile *cf, 
+                               gint n_values, gint before)
+{
+  g_return_val_if_fail(cf, FALSE);
+
+  if ( ! cf->flexifile ) 
+    {
+      cf->flexifile = flexifile_create(n_values);
+      return TRUE;
+    }
+
+  return flexifile_resize(FLEXIFILE(cf->flexifile), n_values, before);
+}
+
index 652ee54106baccb28e36dd44027662251eb508e0..745b709898ea498f7e70255e27b67c47b3dabfde 100644 (file)
@@ -47,13 +47,13 @@ typedef struct _PsppireCaseFile        PsppireCaseFile;
 typedef struct _PsppireCaseFileClass PsppireCaseFileClass;
 
 struct ccase;
 typedef struct _PsppireCaseFileClass PsppireCaseFileClass;
 
 struct ccase;
-struct casefile;
+struct casefilefile;
 
 struct _PsppireCaseFile
 {
   GObject             parent;
 
 
 struct _PsppireCaseFile
 {
   GObject             parent;
 
-  struct casefile *casefile;
+  struct casefile *flexifile;
 };
 
 
 };
 
 
@@ -79,11 +79,23 @@ const union value * psppire_case_file_get_value(const PsppireCaseFile *cf,
 
 struct data_in;
 
 
 struct data_in;
 
-gboolean psppire_case_file_set_value(PsppireCaseFile *cf, gint c, gint idx,
+gboolean psppire_case_file_data_in(PsppireCaseFile *cf, gint c, gint idx,
                                 struct data_in *d_in);
 
                                 struct data_in *d_in);
 
+gboolean psppire_case_file_set_value(PsppireCaseFile *cf, gint casenum, 
+                                    gint idx, union value *v, gint width);
+
 void psppire_case_file_clear(PsppireCaseFile *cf);
 
 void psppire_case_file_clear(PsppireCaseFile *cf);
 
+
+gboolean psppire_case_file_delete_cases(PsppireCaseFile *cf, gint n_rows, 
+                                       gint first);
+
+gboolean psppire_case_file_insert_case(PsppireCaseFile *cf, gint row);
+
+
+gboolean psppire_case_file_insert_values(PsppireCaseFile *cf, gint n_values, gint before);
+
 struct sort_criteria;
 void psppire_case_file_sort(PsppireCaseFile *cf, const struct sort_criteria *);
 
 struct sort_criteria;
 void psppire_case_file_sort(PsppireCaseFile *cf, const struct sort_criteria *);
 
index 6df379731695183763f4a39af44470d0c7e327f3..6c82a30bb5020e82cf90f09b53a932f9c3af206e 100644 (file)
@@ -268,31 +268,49 @@ static void
 insert_variable_callback(GObject *obj, gint var_num, gpointer data)
 {
   PsppireDataStore *store;
 insert_variable_callback(GObject *obj, gint var_num, gpointer data)
 {
   PsppireDataStore *store;
+  gint posn;
 
   g_return_if_fail (data);
 
   store  = PSPPIRE_DATA_STORE(data);
   
 
   g_return_if_fail (data);
 
   store  = PSPPIRE_DATA_STORE(data);
   
-  /* 
-  g_sheet_model_range_changed (G_SHEET_MODEL(store),
-                                casenum, -1,
-                                psppire_case_array_get_n_cases(store->cases),
-                                -1);
-  */
+  if ( var_num > 0 ) 
+    {
+      struct PsppireVariable *variable;
+      variable = psppire_dict_get_variable(store->dict, var_num);
 
 
-#if 0
-  psppire_case_array_resize(store->cases, 
-                        dict_get_next_value_idx (store->dict->dict));
-#endif
+      posn = psppire_variable_get_fv(variable);
+    }
+  else
+    {
+      posn = 0;
+    }
+
+  psppire_case_file_insert_values(store->case_file, 1, posn);
 
   g_sheet_column_columns_changed(G_SHEET_COLUMN(store),
                                  var_num, 1);
 
 
   g_sheet_column_columns_changed(G_SHEET_COLUMN(store),
                                  var_num, 1);
 
-
   g_sheet_model_columns_inserted (G_SHEET_MODEL(store), var_num, 1);
 }
 
 
   g_sheet_model_columns_inserted (G_SHEET_MODEL(store), var_num, 1);
 }
 
 
+static void
+dict_size_change_callback(GObject *obj, 
+                         gint posn, gint adjustment, gpointer data)
+{
+  PsppireDataStore *store ;
+
+  g_return_if_fail (data);
+
+  store  = PSPPIRE_DATA_STORE(data);
+
+  /* 
+  if ( adjustment > 0 )
+  */
+  psppire_case_file_insert_values (store->case_file, adjustment, posn);
+}
+
 
 
 /**
 
 
 /**
@@ -356,7 +374,6 @@ psppire_data_store_set_dictionary(PsppireDataStore *data_store, PsppireDict *dic
                   G_CALLBACK(changed_case_callback), 
                   data_store);
 
                   G_CALLBACK(changed_case_callback), 
                   data_store);
 
-
   g_signal_connect(dict, "variable-inserted", 
                   G_CALLBACK(insert_variable_callback), 
                   data_store);
   g_signal_connect(dict, "variable-inserted", 
                   G_CALLBACK(insert_variable_callback), 
                   data_store);
@@ -365,6 +382,10 @@ psppire_data_store_set_dictionary(PsppireDataStore *data_store, PsppireDict *dic
                   G_CALLBACK(delete_variables_callback), 
                   data_store);
 
                   G_CALLBACK(delete_variables_callback), 
                   data_store);
 
+  g_signal_connect (dict, "dict-size-changed", 
+                   G_CALLBACK(dict_size_change_callback),
+                   data_store);
+
   /* The entire model has changed */
   g_sheet_model_range_changed (G_SHEET_MODEL(data_store), -1, -1, -1, -1);
   
   /* The entire model has changed */
   g_sheet_model_range_changed (G_SHEET_MODEL(data_store), -1, -1, -1, -1);
   
@@ -406,6 +427,8 @@ psppire_data_store_get_string(const GSheetModel *model, gint row, gint column)
 
   v = psppire_case_file_get_value(store->case_file, row, idx);
 
 
   v = psppire_case_file_get_value(store->case_file, row, idx);
 
+  g_return_val_if_fail(v, NULL);
+
   if ( store->show_labels) 
     {
       const struct val_labs * vl = psppire_variable_get_value_labels(pv);
   if ( store->show_labels) 
     {
       const struct val_labs * vl = psppire_variable_get_value_labels(pv);
@@ -455,8 +478,8 @@ psppire_data_store_clear_datum(GSheetModel *model,
   else
     memcpy(v.s, "", MAX_SHORT_STRING);
 
   else
     memcpy(v.s, "", MAX_SHORT_STRING);
 
-  psppire_case_file_set_value(store->case_file, row, index, &v);
-
+  psppire_case_file_set_value(store->case_file, row, index, &v
+                             psppire_variable_get_width(pv));
   return TRUE;
 }
 
   return TRUE;
 }
 
@@ -500,15 +523,7 @@ psppire_data_store_set_string(GSheetModel *model,
     d_in.format = * psppire_variable_get_write_spec(pv);
     d_in.flags = 0;
 
     d_in.format = * psppire_variable_get_write_spec(pv);
     d_in.flags = 0;
 
-    /* 
-    if ( ! data_in(&d_in) ) 
-      {
-       g_warning("Cannot encode string");
-       return FALSE;
-      }
-    */
-
-    psppire_case_file_set_value(store->case_file, row, index, &d_in) ;
+    psppire_case_file_data_in(store->case_file, row, index, &d_in) ;
   }
 
   return TRUE;
   }
 
   return TRUE;
@@ -541,19 +556,6 @@ psppire_data_store_show_labels(PsppireDataStore *store, gboolean show_labels)
 
 
 
 
 
 
-static gboolean 
-write_case(const struct ccase *cc, 
-          gpointer aux)
-{
-  struct sfm_writer *writer = aux;
-
-  if ( ! sfm_write_case(writer, cc) )
-    return FALSE;
-
-
-  return TRUE;
-}
-
 void
 psppire_data_store_create_system_file(PsppireDataStore *store,
                              struct file_handle *handle)
 void
 psppire_data_store_create_system_file(PsppireDataStore *store,
                              struct file_handle *handle)
index 10de849c7b84ab20011a111ff1111a7607adeee1..435be90cb2a86d17cf0fe9f804f3c8d5c44f7da8 100644 (file)
@@ -48,6 +48,7 @@ static void dictionary_tree_model_init(GtkTreeModelIface *iface);
 static GObjectClass     *parent_class = NULL;
 
 enum  {VARIABLE_CHANGED, 
 static GObjectClass     *parent_class = NULL;
 
 enum  {VARIABLE_CHANGED, 
+       VARIABLE_RESIZED,
        VARIABLE_INSERTED,
        VARIABLES_DELETED, 
        n_SIGNALS};
        VARIABLE_INSERTED,
        VARIABLES_DELETED, 
        n_SIGNALS};
@@ -146,6 +147,19 @@ psppire_dict_class_init (PsppireDictClass *class)
                  G_TYPE_INT,
                  G_TYPE_INT);
 
                  G_TYPE_INT,
                  G_TYPE_INT);
 
+
+  signal[VARIABLE_RESIZED] =
+    g_signal_new ("dict-size-changed",
+                 G_TYPE_FROM_CLASS(class),
+                 G_SIGNAL_RUN_FIRST,
+                 0,
+                 NULL, NULL,
+                 gtkextra_VOID__INT_INT,
+                 G_TYPE_NONE, 
+                 2,
+                 G_TYPE_INT,
+                 G_TYPE_INT);
+
 }
 
 static void
 }
 
 static void
@@ -299,6 +313,7 @@ psppire_dict_delete_variables(PsppireDict *d, gint first, gint n)
       var = dict_get_var(d->dict, first);
       dict_delete_var (d->dict, var);
     }
       var = dict_get_var(d->dict, first);
       dict_delete_var (d->dict, var);
     }
+  dict_compact_values(d->dict);
 
   g_signal_emit(d, signal[VARIABLES_DELETED], 0, first, idx );  
 }
 
   g_signal_emit(d, signal[VARIABLES_DELETED], 0, first, idx );  
 }
@@ -446,6 +461,31 @@ psppire_dict_get_next_value_idx (const PsppireDict *dict)
 }
 
 
 }
 
 
+void 
+psppire_dict_resize_variable(PsppireDict *d, const struct PsppireVariable *pv,
+                            gint old_size, gint new_size)
+{
+  gint fv;
+  g_return_if_fail (d);
+  g_return_if_fail (d->dict);
+  
+  if ( old_size == new_size ) 
+    return ;
+
+  pv->v->nv = new_size;
+
+  dict_compact_values(d->dict);
+
+  fv = psppire_variable_get_fv(pv);
+
+  g_signal_emit(d, signal[VARIABLE_RESIZED], 0, 
+               fv + old_size, 
+               new_size - old_size );  
+}
+
+
+
+
 
 /* Tree Model Stuff */
 
 
 /* Tree Model Stuff */
 
index 218cae0d764aebd126ae3729db1266fa3b2a5b6b..e71ef20403daec009fe11456201c04f552211884 100644 (file)
@@ -102,6 +102,10 @@ void psppire_dict_delete_variables(PsppireDict *d, gint first, gint n);
 /* Insert a new variable at posn IDX */
 void psppire_dict_insert_variable(PsppireDict *d, gint idx, const gchar *name);
 
 /* Insert a new variable at posn IDX */
 void psppire_dict_insert_variable(PsppireDict *d, gint idx, const gchar *name);
 
+void psppire_dict_resize_variable(PsppireDict *d, 
+                                 const struct PsppireVariable *pv,
+                                 gint old_size, gint new_size);
+
 gboolean psppire_dict_check_name(const PsppireDict *dict, 
                              const gchar *name, gboolean report);
 
 gboolean psppire_dict_check_name(const PsppireDict *dict, 
                              const gchar *name, gboolean report);
 
index 3fa71ea30b5330fe194df1e6fd8e4745f286b527..5c884a3fb616e558dc77f0766101209b71434924 100644 (file)
@@ -25,6 +25,8 @@
 #include <data/value-labels.h>
 #include <data/format.h>
 
 #include <data/value-labels.h>
 #include <data/format.h>
 
+#include <libpspp/misc.h>
+
 #include "psppire-variable.h"
 #include "psppire-dict.h"
 
 #include "psppire-variable.h"
 #include "psppire-dict.h"
 
@@ -115,8 +117,21 @@ psppire_variable_set_width(struct PsppireVariable *pv, gint width)
   fmt.w = width;
 
   if ( pv->v->type == ALPHA ) 
   fmt.w = width;
 
   if ( pv->v->type == ALPHA ) 
+    {
+      gint old_var_cnt , new_var_cnt ;
+
+      if ( pv->v->width == 0 ) 
+       old_var_cnt = 1;
+      else
+       old_var_cnt = DIV_RND_UP(pv->v->width, MAX_SHORT_STRING);
+      
+      new_var_cnt = DIV_RND_UP(width, MAX_SHORT_STRING);
     pv->v->width = width;
 
     pv->v->width = width;
 
+      psppire_dict_resize_variable(pv->dict, pv,
+                                  old_var_cnt, new_var_cnt);
+    }
+
   return psppire_variable_set_format(pv, &fmt);
 }
 
   return psppire_variable_set_format(pv, &fmt);
 }
 
@@ -124,15 +139,30 @@ psppire_variable_set_width(struct PsppireVariable *pv, gint width)
 gboolean
 psppire_variable_set_type(struct PsppireVariable *pv, int type)
 {
 gboolean
 psppire_variable_set_type(struct PsppireVariable *pv, int type)
 {
+  gint old_var_cnt , new_var_cnt ;
+
   g_return_val_if_fail(pv, FALSE);
   g_return_val_if_fail(pv->dict, FALSE);
   g_return_val_if_fail(pv->v, FALSE);
 
   pv->v->type = type; 
 
   g_return_val_if_fail(pv, FALSE);
   g_return_val_if_fail(pv->dict, FALSE);
   g_return_val_if_fail(pv->v, FALSE);
 
   pv->v->type = type; 
 
+  if ( pv->v->width == 0 ) 
+    old_var_cnt = 1;
+  else
+    old_var_cnt = DIV_RND_UP(pv->v->width, MAX_SHORT_STRING);
+
   if ( type == NUMERIC ) 
     pv->v->width = 0;
 
   if ( type == NUMERIC ) 
     pv->v->width = 0;
 
+  if ( pv->v->width == 0 ) 
+    new_var_cnt = 1;
+  else
+    new_var_cnt = DIV_RND_UP(pv->v->width, MAX_SHORT_STRING);
+
+  psppire_dict_resize_variable(pv->dict, pv,
+                              old_var_cnt, new_var_cnt);
+
   psppire_dict_var_changed(pv->dict, pv->v->index);
   return TRUE;
 }
   psppire_dict_var_changed(pv->dict, pv->v->index);
   return TRUE;
 }
index 162f5bbcf7a045fa2cf2cd880b6ae397a13133b7..67ace893386b2854b9157115e7f71646636dda67 100644 (file)
@@ -111,11 +111,8 @@ main(int argc, char *argv[])
   glade_init();
 
 
   glade_init();
 
 
-  settings_init();
 
 
-  /* 
-  set_pspp_locale("da_DK");
-  */
+  settings_init();
 
   message_dialog_init();
 
 
   message_dialog_init();
 
index 798e62168871255e281c0b6b7d04e77302b34808..5b17aa1d8a75f35134b91841aef328c2d9a280ac 100644 (file)
                  <child>
                    <widget class="GtkImageMenuItem" id="cut1">
                      <property name="visible">True</property>
                  <child>
                    <widget class="GtkImageMenuItem" id="cut1">
                      <property name="visible">True</property>
+                     <property name="sensitive">False</property>
                      <property name="label">gtk-cut</property>
                      <property name="use_stock">True</property>
                      <property name="label">gtk-cut</property>
                      <property name="use_stock">True</property>
-                     <signal name="activate" handler="on_cut1_activate" last_modification_time="Tue, 23 Mar 2004 10:22:51 GMT"/>
                    </widget>
                  </child>
 
                  <child>
                    <widget class="GtkImageMenuItem" id="copy1">
                      <property name="visible">True</property>
                    </widget>
                  </child>
 
                  <child>
                    <widget class="GtkImageMenuItem" id="copy1">
                      <property name="visible">True</property>
+                     <property name="sensitive">False</property>
                      <property name="label">gtk-copy</property>
                      <property name="use_stock">True</property>
                      <property name="label">gtk-copy</property>
                      <property name="use_stock">True</property>
-                     <signal name="activate" handler="on_copy1_activate" last_modification_time="Tue, 23 Mar 2004 10:22:51 GMT"/>
                    </widget>
                  </child>
 
                  <child>
                    <widget class="GtkImageMenuItem" id="paste1">
                      <property name="visible">True</property>
                    </widget>
                  </child>
 
                  <child>
                    <widget class="GtkImageMenuItem" id="paste1">
                      <property name="visible">True</property>
+                     <property name="sensitive">False</property>
                      <property name="label">gtk-paste</property>
                      <property name="use_stock">True</property>
                      <property name="label">gtk-paste</property>
                      <property name="use_stock">True</property>
-                     <signal name="activate" handler="on_paste1_activate" last_modification_time="Tue, 23 Mar 2004 10:22:51 GMT"/>
                    </widget>
                  </child>
 
                  <child>
                    </widget>
                  </child>
 
                  <child>
-                   <widget class="GtkImageMenuItem" id="delete1">
+                   <widget class="GtkMenuItem" id="paste_variables1">
                      <property name="visible">True</property>
                      <property name="visible">True</property>
-                     <property name="label">gtk-delete</property>
-                     <property name="use_stock">True</property>
-                     <signal name="activate" handler="on_delete1_activate" last_modification_time="Tue, 23 Mar 2004 10:22:51 GMT"/>
+                     <property name="sensitive">False</property>
+                     <property name="label" translatable="yes">Paste _Variables</property>
+                     <property name="use_underline">True</property>
                    </widget>
                  </child>
 
                  <child>
                    </widget>
                  </child>
 
                  <child>
-                   <widget class="GtkMenuItem" id="insert1">
+                   <widget class="GtkMenuItem" id="clear1">
                      <property name="visible">True</property>
                      <property name="visible">True</property>
-                     <property name="label" translatable="yes">_Insert</property>
+                     <property name="label" translatable="yes">Cl_ear</property>
                      <property name="use_underline">True</property>
                      <property name="use_underline">True</property>
-                     <signal name="activate" handler="on_insert1_activate" last_modification_time="Mon, 02 Jan 2006 12:46:02 GMT"/>
+                     <signal name="activate" handler="on_clear_activate" last_modification_time="Sun, 02 Jul 2006 07:00:12 GMT"/>
+                     <accelerator key="Delete" modifiers="0" signal="activate"/>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkSeparatorMenuItem" id="separator6">
+                     <property name="visible">True</property>
+                   </widget>
+                 </child>
+
+                 <child>
+                   <widget class="GtkImageMenuItem" id="find1">
+                     <property name="visible">True</property>
+                     <property name="sensitive">False</property>
+                     <property name="label" translatable="yes">_Find</property>
+                     <property name="use_underline">True</property>
+
+                     <child internal-child="image">
+                       <widget class="GtkImage" id="image10">
+                         <property name="visible">True</property>
+                         <property name="stock">gtk-find</property>
+                         <property name="icon_size">1</property>
+                         <property name="xalign">0.5</property>
+                         <property name="yalign">0.5</property>
+                         <property name="xpad">0</property>
+                         <property name="ypad">0</property>
+                       </widget>
+                     </child>
                    </widget>
                  </child>
                </widget>
                    </widget>
                  </child>
                </widget>
                <widget class="GtkMenu" id="menuitem4_menu">
 
                  <child>
                <widget class="GtkMenu" id="menuitem4_menu">
 
                  <child>
-                   <widget class="GtkMenuItem" id="insert_variable1">
+                   <widget class="GtkMenuItem" id="insert-variable">
                      <property name="visible">True</property>
                      <property name="sensitive">False</property>
                      <property name="label" translatable="yes">Insert Variable</property>
                      <property name="use_underline">True</property>
                      <property name="visible">True</property>
                      <property name="sensitive">False</property>
                      <property name="label" translatable="yes">Insert Variable</property>
                      <property name="use_underline">True</property>
+                     <signal name="activate" handler="on_insert_variable" />
                    </widget>
                  </child>
 
                  <child>
                    </widget>
                  </child>
 
                  <child>
-                   <widget class="GtkMenuItem" id="insert_cases1">
+                   <widget class="GtkMenuItem" id="insert-cases">
                      <property name="visible">True</property>
                      <property name="visible">True</property>
-                     <property name="sensitive">False</property>
                      <property name="label" translatable="yes">Insert Cases</property>
                      <property name="use_underline">True</property>
                      <property name="label" translatable="yes">Insert Cases</property>
                      <property name="use_underline">True</property>
+                     <signal name="activate" handler="on_insert_cases" last_modification_time="Sat, 01 Jul 2006 08:40:06 GMT"/>
                    </widget>
                  </child>
 
                    </widget>
                  </child>
 
                      <signal name="activate" handler="on_go_to_case_activate" last_modification_time="Mon, 19 Jun 2006 10:29:37 GMT"/>
 
                      <child internal-child="image">
                      <signal name="activate" handler="on_go_to_case_activate" last_modification_time="Mon, 19 Jun 2006 10:29:37 GMT"/>
 
                      <child internal-child="image">
-                       <widget class="GtkImage" id="image1">
+                       <widget class="GtkImage" id="image11">
                          <property name="visible">True</property>
                          <property name="stock">gtk-jump-to</property>
                          <property name="icon_size">1</property>
                          <property name="visible">True</property>
                          <property name="stock">gtk-jump-to</property>
                          <property name="icon_size">1</property>
                  <property name="visible_horizontal">True</property>
                  <property name="visible_vertical">True</property>
                  <property name="is_important">False</property>
                  <property name="visible_horizontal">True</property>
                  <property name="visible_vertical">True</property>
                  <property name="is_important">False</property>
+                 <signal name="clicked" handler="on_insert_case_clicked" last_modification_time="Sat, 01 Jul 2006 08:41:07 GMT"/>
                </widget>
                <packing>
                  <property name="expand">False</property>
                </widget>
                <packing>
                  <property name="expand">False</property>
index b40179bb5192067ef592793045c76d2793e239b1..ecdcb3c2e68d72c77533fe1e917ce12504840b34 100644 (file)
@@ -20,6 +20,7 @@ src_ui_terminal_pspp_LDADD =                                  \
        src/ui/terminal/libui.a \
        src/language/liblanguage.a \
        src/language/tests/libtests.a \
        src/ui/terminal/libui.a \
        src/language/liblanguage.a \
        src/language/tests/libtests.a \
+       src/ui/libuicommon.a \
        src/language/utilities/libutilities.a \
        src/language/control/libcontrol.a \
        src/language/expressions/libexpressions.a \
        src/language/utilities/libutilities.a \
        src/language/control/libcontrol.a \
        src/language/expressions/libexpressions.a \