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"
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"
"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/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
-#: 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 "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 ""
-#: 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 ""
-#: 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 ""
-#: 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 "
"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 ""
-#: 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 ""
-#: src/language/data-io/get.c:683
+#: src/language/data-io/get.c:684
#, 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 ""
-#: 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 ""
-#: 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 ""
-#: 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 ""
-#: 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 ""
-#: src/language/data-io/get.c:961
+#: src/language/data-io/get.c:962
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 ""
-#: src/language/data-io/get.c:995
+#: src/language/data-io/get.c:996
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 ""
-#: src/language/data-io/get.c:1050
+#: src/language/data-io/get.c:1051
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 ""
-#: 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 ""
-#: 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 "
msgid "Coefficient Correlations"
msgstr ""
-#: src/language/stats/regression.q:1122
+#: src/language/stats/regression.q:1129
msgid "Dependent variable must be numeric."
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 "
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"
-#: src/ui/gui/menu-actions.c:230
+#: src/ui/gui/menu-actions.c:228
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)"
-#: src/ui/gui/menu-actions.c:242
+#: src/ui/gui/menu-actions.c:240
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"
-#: src/ui/gui/menu-actions.c:566
+#: src/ui/gui/menu-actions.c:568
msgid "Font Selection"
msgstr "Schriftwahlung"
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 ""
-#: 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
msgid "_Edit"
msgstr "_Bearbeiten"
+#: src/ui/gui/psppire.glade:140
+#, fuzzy
+msgid "Paste _Variables"
+msgstr "Variableansicht"
+
#: 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"
-#: src/ui/gui/psppire.glade:170
+#: src/ui/gui/psppire.glade:198
msgid "Status Bar"
msgstr "Statusleiste"
-#: src/ui/gui/psppire.glade:179
+#: src/ui/gui/psppire.glade:207
msgid "Toolbars"
msgstr "Werkzeugregal"
-#: src/ui/gui/psppire.glade:193
+#: src/ui/gui/psppire.glade:221
msgid "Fonts"
msgstr "Schrift"
-#: src/ui/gui/psppire.glade:202
+#: src/ui/gui/psppire.glade:230
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"
-#: src/ui/gui/psppire.glade:228
+#: src/ui/gui/psppire.glade:256
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"
-#: src/ui/gui/psppire.glade:250
+#: src/ui/gui/psppire.glade:278
#, 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"
-#: src/ui/gui/psppire.glade:269
+#: src/ui/gui/psppire.glade:297
#, 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 ""
-#: 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 ""
-#: src/ui/gui/psppire.glade:314
+#: src/ui/gui/psppire.glade:343
msgid "Transpose"
msgstr ""
-#: src/ui/gui/psppire.glade:323
+#: src/ui/gui/psppire.glade:352
msgid "Restructure"
msgstr ""
-#: src/ui/gui/psppire.glade:332
+#: src/ui/gui/psppire.glade:361
#, fuzzy
msgid "Merge Files"
msgstr "Alle Datei"
-#: src/ui/gui/psppire.glade:341
+#: src/ui/gui/psppire.glade:370
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"
-#: 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 ""
-#: 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 ""
-#: src/ui/gui/psppire.glade:386
+#: src/ui/gui/psppire.glade:415
msgid "_Help"
msgstr "_Hilfe"
-#: src/ui/gui/psppire.glade:395
+#: src/ui/gui/psppire.glade:424
msgid "_About"
msgstr "_Info"
-#: src/ui/gui/psppire.glade:446
+#: src/ui/gui/psppire.glade:475
msgid "Save"
msgstr "Speichen"
-#: src/ui/gui/psppire.glade:462
+#: src/ui/gui/psppire.glade:491
msgid "Print"
msgstr "Drucken"
-#: src/ui/gui/psppire.glade:490
+#: src/ui/gui/psppire.glade:519
msgid "Undo"
msgstr ""
-#: src/ui/gui/psppire.glade:505
+#: src/ui/gui/psppire.glade:534
msgid "Redo"
msgstr ""
-#: src/ui/gui/psppire.glade:579
+#: src/ui/gui/psppire.glade:608
msgid "Find"
msgstr ""
-#: src/ui/gui/psppire.glade:607
+#: src/ui/gui/psppire.glade:636
#, fuzzy
msgid "Insert Case"
msgstr "_Stecken"
-#: src/ui/gui/psppire.glade:738
+#: src/ui/gui/psppire.glade:768
msgid "Use Sets"
msgstr ""
-#: src/ui/gui/psppire.glade:870
+#: src/ui/gui/psppire.glade:900
msgid "Data View"
msgstr "Datenansicht"
-#: src/ui/gui/psppire.glade:918
+#: src/ui/gui/psppire.glade:948
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."
-#: 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"
" 02110-1301, USA.\n"
msgstr ""
-#: src/ui/gui/psppire.glade:990
+#: src/ui/gui/psppire.glade:1020
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"
-#: 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"
-#: src/ui/gui/psppire.glade:1082
+#: src/ui/gui/psppire.glade:1112
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"
-#: 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"
-#: src/ui/gui/psppire.glade:1142
+#: src/ui/gui/psppire.glade:1172
msgid "Custom currency"
msgstr "Spezialwährung"
-#: src/ui/gui/psppire.glade:1287
+#: src/ui/gui/psppire.glade:1317
msgid "positive"
msgstr "positiv"
-#: src/ui/gui/psppire.glade:1312
+#: src/ui/gui/psppire.glade:1342
msgid "negative"
msgstr "negativ"
-#: src/ui/gui/psppire.glade:1341
+#: src/ui/gui/psppire.glade:1371
msgid "Sample"
msgstr "Muster"
-#: src/ui/gui/psppire.glade:1419
+#: src/ui/gui/psppire.glade:1449
msgid "Decimal Places:"
msgstr "Dezimalstellen:"
-#: src/ui/gui/psppire.glade:1496
+#: src/ui/gui/psppire.glade:1526
msgid "Width:"
msgstr "Große:"
-#: src/ui/gui/psppire.glade:1715
+#: src/ui/gui/psppire.glade:1745
msgid "Value:"
msgstr "Werte:"
-#: src/ui/gui/psppire.glade:1743
+#: src/ui/gui/psppire.glade:1773
msgid "Value Label:"
msgstr "Kennsatz:"
-#: src/ui/gui/psppire.glade:1959
+#: src/ui/gui/psppire.glade:1989
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"
-#: src/ui/gui/psppire.glade:2070
+#: src/ui/gui/psppire.glade:2100
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"
-#: src/ui/gui/psppire.glade:2236
+#: src/ui/gui/psppire.glade:2266
msgid "_Low:"
msgstr "_Tief:"
-#: src/ui/gui/psppire.glade:2295
+#: src/ui/gui/psppire.glade:2325
msgid "_High:"
msgstr "_Hoch:"
-#: src/ui/gui/psppire.glade:2369
+#: src/ui/gui/psppire.glade:2399
msgid "Di_screte value:"
msgstr "Di_skretwerte"
-#: src/ui/gui/psppire.glade:2495
+#: src/ui/gui/psppire.glade:2525
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 ""
-#: 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 ""
-#: src/ui/gui/psppire.glade:2707
+#: src/ui/gui/psppire.glade:2737
msgid "Sort Order"
msgstr ""
-#: src/ui/gui/psppire.glade:2744
+#: src/ui/gui/psppire.glade:2774
msgid "Sort by:"
msgstr ""
#: src/ui/terminal/msg-ui.c:117
msgid "warning"
msgstr "Warnung"
+
+#~ msgid "_Insert"
+#~ msgstr "_Stecken"
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"
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"
"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/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
-#: 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 "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 ""
-#: 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 ""
-#: 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 ""
-#: 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 "
"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 ""
-#: 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 ""
-#: src/language/data-io/get.c:683
+#: src/language/data-io/get.c:684
#, 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 ""
-#: 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 ""
-#: 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 ""
-#: 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 ""
-#: 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 ""
-#: src/language/data-io/get.c:961
+#: src/language/data-io/get.c:962
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 ""
-#: src/language/data-io/get.c:995
+#: src/language/data-io/get.c:996
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 ""
-#: src/language/data-io/get.c:1050
+#: src/language/data-io/get.c:1051
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 ""
-#: 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 ""
-#: 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 "
msgid "Coefficient Correlations"
msgstr ""
-#: src/language/stats/regression.q:1122
+#: src/language/stats/regression.q:1129
msgid "Dependent variable must be numeric."
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 "
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 ""
-#: src/ui/gui/menu-actions.c:230
+#: src/ui/gui/menu-actions.c:228
msgid "System Files (*.sav)"
msgstr ""
-#: src/ui/gui/menu-actions.c:236
+#: src/ui/gui/menu-actions.c:234
msgid "Portable Files (*.por) "
msgstr ""
-#: src/ui/gui/menu-actions.c:242
+#: src/ui/gui/menu-actions.c:240
msgid "All Files"
msgstr ""
-#: src/ui/gui/menu-actions.c:274
+#: src/ui/gui/menu-actions.c:272
msgid "Save Data As"
msgstr ""
-#: src/ui/gui/menu-actions.c:566
+#: src/ui/gui/menu-actions.c:568
msgid "Font Selection"
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 ""
-#: 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
msgid "_Edit"
msgstr ""
+#: src/ui/gui/psppire.glade:140
+msgid "Paste _Variables"
+msgstr ""
+
#: src/ui/gui/psppire.glade:148
-msgid "_Insert"
+msgid "Cl_ear"
+msgstr ""
+
+#: src/ui/gui/psppire.glade:165
+msgid "_Find"
msgstr ""
-#: src/ui/gui/psppire.glade:161
+#: src/ui/gui/psppire.glade:189
msgid "_View"
msgstr ""
-#: src/ui/gui/psppire.glade:170
+#: src/ui/gui/psppire.glade:198
msgid "Status Bar"
msgstr ""
-#: src/ui/gui/psppire.glade:179
+#: src/ui/gui/psppire.glade:207
msgid "Toolbars"
msgstr ""
-#: src/ui/gui/psppire.glade:193
+#: src/ui/gui/psppire.glade:221
msgid "Fonts"
msgstr ""
-#: src/ui/gui/psppire.glade:202
+#: src/ui/gui/psppire.glade:230
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 ""
-#: src/ui/gui/psppire.glade:228
+#: src/ui/gui/psppire.glade:256
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 ""
-#: src/ui/gui/psppire.glade:250
+#: src/ui/gui/psppire.glade:278
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 ""
-#: src/ui/gui/psppire.glade:269
+#: src/ui/gui/psppire.glade:297
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 ""
-#: 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 ""
-#: src/ui/gui/psppire.glade:314
+#: src/ui/gui/psppire.glade:343
msgid "Transpose"
msgstr ""
-#: src/ui/gui/psppire.glade:323
+#: src/ui/gui/psppire.glade:352
msgid "Restructure"
msgstr ""
-#: src/ui/gui/psppire.glade:332
+#: src/ui/gui/psppire.glade:361
msgid "Merge Files"
msgstr ""
-#: src/ui/gui/psppire.glade:341
+#: src/ui/gui/psppire.glade:370
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 ""
-#: 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 ""
-#: 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 ""
-#: src/ui/gui/psppire.glade:386
+#: src/ui/gui/psppire.glade:415
msgid "_Help"
msgstr ""
-#: src/ui/gui/psppire.glade:395
+#: src/ui/gui/psppire.glade:424
msgid "_About"
msgstr ""
-#: src/ui/gui/psppire.glade:446
+#: src/ui/gui/psppire.glade:475
msgid "Save"
msgstr ""
-#: src/ui/gui/psppire.glade:462
+#: src/ui/gui/psppire.glade:491
msgid "Print"
msgstr ""
-#: src/ui/gui/psppire.glade:490
+#: src/ui/gui/psppire.glade:519
msgid "Undo"
msgstr ""
-#: src/ui/gui/psppire.glade:505
+#: src/ui/gui/psppire.glade:534
msgid "Redo"
msgstr ""
-#: src/ui/gui/psppire.glade:579
+#: src/ui/gui/psppire.glade:608
msgid "Find"
msgstr ""
-#: src/ui/gui/psppire.glade:607
+#: src/ui/gui/psppire.glade:636
msgid "Insert Case"
msgstr ""
-#: src/ui/gui/psppire.glade:738
+#: src/ui/gui/psppire.glade:768
msgid "Use Sets"
msgstr ""
-#: src/ui/gui/psppire.glade:870
+#: src/ui/gui/psppire.glade:900
msgid "Data View"
msgstr ""
-#: src/ui/gui/psppire.glade:918
+#: src/ui/gui/psppire.glade:948
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 ""
-#: 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"
" 02110-1301, USA.\n"
msgstr ""
-#: src/ui/gui/psppire.glade:990
+#: src/ui/gui/psppire.glade:1020
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 ""
-#: 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 ""
-#: src/ui/gui/psppire.glade:1082
+#: src/ui/gui/psppire.glade:1112
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 ""
-#: 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 ""
-#: src/ui/gui/psppire.glade:1142
+#: src/ui/gui/psppire.glade:1172
msgid "Custom currency"
msgstr ""
-#: src/ui/gui/psppire.glade:1287
+#: src/ui/gui/psppire.glade:1317
msgid "positive"
msgstr ""
-#: src/ui/gui/psppire.glade:1312
+#: src/ui/gui/psppire.glade:1342
msgid "negative"
msgstr ""
-#: src/ui/gui/psppire.glade:1341
+#: src/ui/gui/psppire.glade:1371
msgid "Sample"
msgstr ""
-#: src/ui/gui/psppire.glade:1419
+#: src/ui/gui/psppire.glade:1449
msgid "Decimal Places:"
msgstr ""
-#: src/ui/gui/psppire.glade:1496
+#: src/ui/gui/psppire.glade:1526
msgid "Width:"
msgstr ""
-#: src/ui/gui/psppire.glade:1715
+#: src/ui/gui/psppire.glade:1745
msgid "Value:"
msgstr ""
-#: src/ui/gui/psppire.glade:1743
+#: src/ui/gui/psppire.glade:1773
msgid "Value Label:"
msgstr ""
-#: src/ui/gui/psppire.glade:1959
+#: src/ui/gui/psppire.glade:1989
msgid "Missing Values"
msgstr ""
-#: src/ui/gui/psppire.glade:2045
+#: src/ui/gui/psppire.glade:2075
msgid "_No missing values"
msgstr ""
-#: src/ui/gui/psppire.glade:2070
+#: src/ui/gui/psppire.glade:2100
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 ""
-#: src/ui/gui/psppire.glade:2236
+#: src/ui/gui/psppire.glade:2266
msgid "_Low:"
msgstr ""
-#: src/ui/gui/psppire.glade:2295
+#: src/ui/gui/psppire.glade:2325
msgid "_High:"
msgstr ""
-#: src/ui/gui/psppire.glade:2369
+#: src/ui/gui/psppire.glade:2399
msgid "Di_screte value:"
msgstr ""
-#: src/ui/gui/psppire.glade:2495
+#: src/ui/gui/psppire.glade:2525
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 ""
-#: 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 ""
-#: src/ui/gui/psppire.glade:2707
+#: src/ui/gui/psppire.glade:2737
msgid "Sort Order"
msgstr ""
-#: src/ui/gui/psppire.glade:2744
+#: src/ui/gui/psppire.glade:2774
msgid "Sort by:"
msgstr ""
# 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
-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)\"
+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
-## Process this file with automake to produce Makefile.in -*- makefile -*-
noinst_LIBRARIES += src/data/libdata.a
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.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 \
--- /dev/null
+/* 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
/* 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
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 <string.h>
-#include <unistd.h>
-#include <libpspp/alloc.h>
+#include <assert.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. */
-/* 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
-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
-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
-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
- 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 *
-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
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);
}
/* 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
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>
+
struct ccase;
-struct casefile;
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
--- /dev/null
+/* 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,
+ };
--- /dev/null
+/* 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
#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>
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;
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;
#include <stdlib.h>
#include "case.h"
#include "casefile.h"
+#include "fastfile.h"
#include "dictionary.h"
#include "file-handle-def.h"
#include "scratch-handle.h"
/* 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);
{
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;
#include <data/case-source.h>
#include <data/case.h>
#include <data/casefile.h>
+#include <data/fastfile.h>
#include "xalloc.h"
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. */
#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>
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));
#include <config.h>
#include <data/casefile.h>
+#include <data/fastfile.h>
+
#include <data/case.h>
#include <gsl/gsl_randist.h>
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++)
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);
#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>
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);
{
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);
}
}
/* Create output file. */
- output = casefile_create (xsrt->value_cnt);
+ output = fastfile_create (xsrt->value_cnt);
casefile_to_disk (output);
/* Merge. */
+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
--- /dev/null
+## 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
--- /dev/null
+/* 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;
+}
+
+
+
--- /dev/null
+/* 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
+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
$(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 \
g_warning("Cannot write case to casefile\n");
break;
}
-
case_destroy(&c);
-
}
sfm_close_reader(reader);
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)
}
}
+#if 0
void
on_delete1_activate (GtkMenuItem *menuitem,
gpointer user_data)
break;
}
}
-
+#endif
void
on_about1_activate(GtkMenuItem *menuitem,
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]);
+ 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]);
+ gtk_widget_set_sensitive(insert_variable, FALSE);
+ gtk_widget_set_sensitive(insert_cases, TRUE);
break;
default:
g_assert_not_reached();
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);
+}
+
+
#include <gtksheet/gtkextra-marshal.h>
#include <data/case.h>
+#include <ui/flexifile.h>
#include <data/casefile.h>
#include <data/data-in.h>
-
#include <math/sort.h>
+#include <libpspp/misc.h>
/* --- prototypes --- */
static void psppire_case_file_class_init (PsppireCaseFileClass *class);
{
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);
}
static void
psppire_case_file_init (PsppireCaseFile *cf)
{
- cf->casefile = 0;
+ cf->flexifile = 0;
}
/**
* 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);
- cf->casefile = casefile_create(var_cnt);
+ cf->flexifile = flexifile_create(val_cnt);
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
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_return_val_if_fail(cf, FALSE);
- if ( ! cf->casefile)
+ if ( ! cf->flexifile)
return 0;
- return casefile_get_case_cnt(cf->casefile);
+ return casefile_get_case_cnt(cf->flexifile);
}
/* Return the IDXth value from case CASENUM.
{
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);
- casereader_destroy(reader);
+
case_destroy(&c);
return v;
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);
}
-/* 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,
+ 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 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);
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;
void
psppire_case_file_sort(PsppireCaseFile *cf, const struct sort_criteria *sc)
{
+ struct ccase cc;
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 */
- 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);
}
+
+
+/* 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);
+}
+
typedef struct _PsppireCaseFileClass PsppireCaseFileClass;
struct ccase;
-struct casefile;
+struct casefilefile;
struct _PsppireCaseFile
{
GObject parent;
- struct casefile *casefile;
+ struct casefile *flexifile;
};
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);
+gboolean psppire_case_file_set_value(PsppireCaseFile *cf, gint casenum,
+ gint idx, union value *v, gint width);
+
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 *);
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_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_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);
+}
+
/**
G_CALLBACK(changed_case_callback),
data_store);
-
g_signal_connect(dict, "variable-inserted",
G_CALLBACK(insert_variable_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);
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);
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;
}
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;
-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)
static GObjectClass *parent_class = NULL;
enum {VARIABLE_CHANGED,
+ VARIABLE_RESIZED,
VARIABLE_INSERTED,
VARIABLES_DELETED,
n_SIGNALS};
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
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 );
}
}
+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 */
/* 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);
#include <data/value-labels.h>
#include <data/format.h>
+#include <libpspp/misc.h>
+
#include "psppire-variable.h"
#include "psppire-dict.h"
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;
+ psppire_dict_resize_variable(pv->dict, pv,
+ old_var_cnt, new_var_cnt);
+ }
+
return psppire_variable_set_format(pv, &fmt);
}
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;
+ 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 ( 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;
}
glade_init();
- settings_init();
- /*
- set_pspp_locale("da_DK");
- */
+ settings_init();
message_dialog_init();
<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>
- <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>
+ <property name="sensitive">False</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>
+ <property name="sensitive">False</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 class="GtkImageMenuItem" id="delete1">
+ <widget class="GtkMenuItem" id="paste_variables1">
<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 class="GtkMenuItem" id="insert1">
+ <widget class="GtkMenuItem" id="clear1">
<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>
- <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 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>
+ <signal name="activate" handler="on_insert_variable" />
</widget>
</child>
<child>
- <widget class="GtkMenuItem" id="insert_cases1">
+ <widget class="GtkMenuItem" id="insert-cases">
<property name="visible">True</property>
- <property name="sensitive">False</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>
<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_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>
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 \