Merge 'master' into 'psppsheet'. 20121231032129/pspp 20130101032123/pspp 20130102032118/pspp 20130103032118/pspp 20130104032103/pspp 20130105032146/pspp 20130106032112/pspp
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 30 Dec 2012 03:21:39 +0000 (19:21 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 30 Dec 2012 03:21:39 +0000 (19:21 -0800)
82 files changed:
README.Git
Smake
doc/data-io.texi
glade/acr.c
glade/bbox.c
glade/dialog.c
glade/selector.c
po/de.po
src/data/attributes.c
src/data/csv-file-writer.c
src/data/data-in.c
src/data/data-out.c
src/data/dictionary.c
src/data/file-handle-def.c
src/data/format.c
src/data/gnumeric-reader.c
src/data/psql-reader.c
src/data/session.c
src/data/sys-file-reader.c
src/data/sys-file-writer.c
src/data/value.c
src/data/value.h
src/data/variable.c
src/data/vector.c
src/language/control/repeat.c
src/language/data-io/combine-files.c
src/language/data-io/placement-parser.c
src/language/data-io/placement-parser.h
src/language/data-io/print.c
src/language/dictionary/modify-variables.c
src/language/dictionary/mrsets.c
src/language/dictionary/vector.c
src/language/expressions/parse.c
src/language/lexer/scan.c
src/language/lexer/token.c
src/language/lexer/variable-parser.c
src/language/stats/aggregate.c
src/language/stats/autorecode.c
src/language/stats/descriptives.c
src/language/stats/npar.c
src/libpspp/automake.mk
src/libpspp/hash-functions.c
src/libpspp/hash-functions.h
src/libpspp/i18n.c
src/libpspp/i18n.h
src/libpspp/misc.c
src/libpspp/misc.h
src/libpspp/str.c
src/libpspp/str.h
src/libpspp/stringi-map.c
src/libpspp/stringi-set.c
src/libpspp/u8-line.c [new file with mode: 0644]
src/libpspp/u8-line.h [new file with mode: 0644]
src/output/ascii.c
src/output/ascii.h
src/output/measure.c
src/ui/gui/aggregate-dialog.c
src/ui/gui/chi-square-dialog.c
src/ui/gui/count-dialog.c
src/ui/gui/oneway-anova-dialog.c
src/ui/gui/psppire-dialog-action-binomial.c
src/ui/gui/psppire-dialog-action-factor.c
src/ui/gui/psppire-dialog-action-frequencies.c
src/ui/gui/psppire-dialog-action-logistic.c
src/ui/gui/psppire-output-window.c
src/ui/gui/psppire-val-chooser.c
src/ui/gui/psppire-val-chooser.h
src/ui/gui/psppire-var-view.c
src/ui/gui/psppire-var-view.h
src/ui/gui/recode-dialog.c
src/ui/gui/select-cases-dialog.c
src/ui/gui/t-test-options.c
src/ui/syntax-gen.c
tests/automake.mk
tests/data/dictionary.at [new file with mode: 0644]
tests/data/sys-file-reader.at
tests/language/data-io/update.at
tests/language/stats/npar.at
tests/libpspp/stringi-map-test.c
tests/libpspp/stringi-set-test.c
tests/output/ascii.at
tests/output/render-test.c

index 98ea98242798cf946449e0b7d4a511b828d26770..33337ed2bb9085ba7c465f961caa116db5c83836 100644 (file)
@@ -34,12 +34,12 @@ This version of PSPP should work with the Gnulib commit shown below.
 Gnulib does not maintain a stable API or ABI, so it is possible that
 PSPP will not work with older or newer versions of Gnulib.
 
-       commit 392f10a02b1effd254f3a751e42daccf929c3efa
-       Author: John Darrington <john@darrington.wattle.id.au>
-       Date:   Sun Jul 29 00:30:48 2012 +0200
+       commit f022473fdaf724d84817c4003120b9a38fbf884b
+       Author: Ben Pfaff <blp@cs.stanford.edu>
+       Date:   Tue Dec 18 21:06:17 2012 -0800
+
+           New 'c-*printf' modules for formatted output in C locale.
 
-           clean-temp: Fix memory leak.
-           
 
 To clone Gnulib into a directory named "gnulib" using Git, and then
 check out this particular commit, run these commands:
diff --git a/Smake b/Smake
index 61235efd90c19a39113e1c85633fddb6befe1975..72ec791a5e2c615b6715ef0bcf17f302cbd62745 100644 (file)
--- a/Smake
+++ b/Smake
@@ -7,10 +7,12 @@ GNULIB_TOOL = $(GNULIB)/gnulib-tool
 GNULIB_MODULES = \
        assert \
        byteswap \
+       c-snprintf \
        c-strcase \
        c-strcasestr \
        c-ctype \
        c-strtod \
+       c-xvasprintf \
        clean-temp \
        close \
        configmake \
@@ -74,11 +76,16 @@ GNULIB_MODULES = \
        sys_stat \
        tempname \
        trunc \
+       unicase/u8-casecmp \
+       unicase/u8-casefold \
+       unicase/u8-tolower \
+       unicase/u8-toupper \
        unictype/ctype-print \
        unictype/property-id-continue \
        unictype/property-id-start \
        unigbrk/uc-is-grapheme-break \
        unilbrk/u8-possible-linebreaks \
+       uninorm/nfkd \
        unistd \
        unistr/u8-check \
        unistr/u8-cpy \
index da1712e3d46a7b9c322cf772aeb337f895096617..88577a48e2cc457ec755c8b5210a8960bb0ae0c6 100644 (file)
@@ -963,7 +963,7 @@ PRINT
         [/[@var{line_no}] @var{arg}@dots{}]
 
 @var{arg} takes one of the following forms:
-        '@var{string}' [@var{start}-@var{end}]
+        '@var{string}' [@var{start}]
         @var{var_list} @var{start}-@var{end} [@var{type_spec}]
         @var{var_list} (@var{fortran_spec})
         @var{var_list} *
@@ -1003,11 +1003,10 @@ line number, the next line number will be specified.  Multiple lines may
 be specified using multiple slashes with the intended output for a line
 following its respective slash.
 
-Literal strings may be printed.  Specify the string itself.  Optionally
-the string may be followed by a column number or range of column
-numbers, specifying the location on the line for the string to be
-printed.  Otherwise, the string will be printed at the current position
-on the line.
+Literal strings may be printed.  Specify the string itself.
+Optionally the string may be followed by a column number, specifying
+the column on the line where the string should start.  Otherwise, the
+string will be printed at the current position on the line.
 
 Variables to be printed can be specified in the same ways as available
 for @cmd{DATA LIST FIXED} (@pxref{DATA LIST FIXED}).  In addition, a
index 397dc5d812cab4cafbe609ac60066df352c60d9c..c6ee0ef639b5f799c12fc2a281f08f771688301c 100644 (file)
@@ -1,3 +1,4 @@
+#include <config.h>
 #include <glib.h>
 #include <gtk/gtk.h>
 
index 46ed7fbd0207f6f8783299d9d597d85106cd0670..2e675c41b2400efbd56ce60dc68b0ac8069fb3a8 100644 (file)
@@ -1,3 +1,4 @@
+#include <config.h>
 #include <gladeui/glade.h>
 #include <gtk/gtk.h>
 
index 09330bd787955de63a4c5ccff9bce47321e716e7..7b13226e01d25203fb748119f61360d25014c7ae 100644 (file)
@@ -1,3 +1,4 @@
+#include <config.h>
 #include <glib.h>
 #include <gtk/gtk.h>
 
index d2b21a99725e6e903709aa0ca76402ea5bffc48f..6b2e4d7938c0bcb6e82c47a1fdfe24a6fd000334 100644 (file)
@@ -1,3 +1,4 @@
+#include <config.h>
 #include <glib.h>
 #include <gtk/gtk.h>
 
index 84371ee6c3a20b2250bc87c7860e560754820bf1..d9283e95c75fb5e0997bd89d802690fe4f7e1360 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -1,28 +1,28 @@
 # German translation for PSPP
 # Copyright (C) 2012 Free Software Foundation, Inc.
 # This file is distributed under the same license as the pspp package.
-# Matthias Keil <matthias.keil@uni-jena.de>, 2012.
+# Matthias Keil <matthias.keil@ymail.com>, - 2012
+# Stefan Grotz <stefan.grotz@uni-jena.de>, 2012 -
 #
 msgid ""
 msgstr ""
 "Project-Id-Version: pspp 0.7.9\n"
 "Report-Msgid-Bugs-To: pspp-dev@gnu.org\n"
 "POT-Creation-Date: 2012-02-03 19:12-0800\n"
-"PO-Revision-Date: 2012-02-29 10:29+0100\n"
-"Last-Translator: Matthias Keil <matthias.keil@uni-jena.de>\n"
+"PO-Revision-Date: 2012-12-17 19:01+0100\n"
+"Last-Translator: Matthias Keil <matthias.keil@ymail.com>\n"
 "Language-Team: German <translation-team-de@lists.sourceforge.net>\n"
-"Language: \n"
+"Language: de_DE\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1)\n"
-"X-Poedit-Language: German\n"
-"X-Poedit-Country: GERMANY\n"
-"X-Poedit-SourceCharset: utf-8\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Generator: Poedit 1.5.4\n"
 
 #: src/ui/gui/helper.c:153
 msgid "Sorry. The help system hasn't yet been implemented."
-msgstr ""
+msgstr "Entschuldigung. Das Hilfesystem wurde noch nicht implementiert.  "
 
 #: src/ui/gui/psppire-buttonbox.c:329 src/ui/gui/psppire-buttonbox.c:487
 msgid "Continue"
@@ -88,17 +88,17 @@ msgstr ""
 #: src/data/calendar.c:100
 #, c-format
 msgid "Month %d is not in acceptable range of 0 to 13."
-msgstr ""
+msgstr "Der Monat  %d ist nicht im akzeptierten Bereich von 0 bis 13."
 
 #: src/data/calendar.c:110
 #, c-format
 msgid "Day %d is not in acceptable range of 0 to 31."
-msgstr ""
+msgstr "Der Tag %d ist nicht im akzeptierten Bereich von 0 bis 31"
 
 #: src/data/calendar.c:119
 #, c-format
 msgid "Date %04d-%d-%d is before the earliest acceptable date of 1582-10-15."
-msgstr ""
+msgstr "Das Datum %04d-%d-%d ist vor dem frühsten akzeptierten Datum 1582-10-15."
 
 #: src/data/casereader-filter.c:221
 msgid "At least one case in the data read had a weight value that was user-missing, system-missing, zero, or negative.  These case(s) were ignored."
@@ -108,7 +108,7 @@ msgstr ""
 #. that identify types of files.
 #: src/data/csv-file-writer.c:152
 msgid "CSV file"
-msgstr ""
+msgstr "CSV Datei"
 
 #: src/data/csv-file-writer.c:161 src/data/sys-file-writer.c:225
 #, c-format
@@ -135,7 +135,7 @@ msgstr ""
 
 #: src/data/data-in.c:391
 msgid "Invalid numeric syntax."
-msgstr ""
+msgstr "Ungültige numerische Syntax."
 
 #: src/data/data-in.c:399 src/data/data-in.c:570
 msgid "Too-large number set to system-missing."
@@ -176,7 +176,7 @@ msgstr ""
 #: src/data/data-in.c:782
 #, c-format
 msgid "Day (%ld) must be between 1 and 31."
-msgstr ""
+msgstr "Tag (%ld) muss zwischen 1 und 31 liegen."
 
 #: src/data/data-in.c:827
 msgid "Delimiter expected between fields in date."
@@ -184,12 +184,12 @@ msgstr ""
 
 #: src/data/data-in.c:901
 msgid "Unrecognized month format.  Months may be specified as Arabic or Roman numerals or as at least 3 letters of their English names."
-msgstr ""
+msgstr "Unbekanntes Monatsformat. Monate müssen mit arabischen oder romanischen Zahlen oder mindestens den ersten 3 Buchstaben ihrer englischen Namen bezeichnet werden."
 
 #: src/data/data-in.c:928
 #, c-format
 msgid "Year (%ld) must be between 1582 and 19999."
-msgstr ""
+msgstr "Jahr (%ld) muss zwischen 1582 und 19999 liegen."
 
 #: src/data/data-in.c:939
 #, c-format
@@ -213,7 +213,7 @@ msgstr ""
 #: src/data/data-in.c:1000
 #, c-format
 msgid "Week (%ld) must be between 1 and 53."
-msgstr ""
+msgstr "Woche (%ld) muss zwischen 1 und 53 liegen."
 
 #: src/data/data-in.c:1012
 msgid "Delimiter expected between fields in time."
@@ -222,7 +222,7 @@ msgstr ""
 #: src/data/data-in.c:1032
 #, c-format
 msgid "Minute (%ld) must be between 0 and 59."
-msgstr ""
+msgstr "Minute (%ld) muss zwischen 0 und 59 liegen."
 
 #: src/data/data-in.c:1070
 msgid "Unrecognized weekday name.  At least the first two letters of an English weekday name must be specified."
@@ -236,12 +236,12 @@ msgstr ""
 #: src/data/data-out.c:546
 #, c-format
 msgid "Weekday number %f is not between 1 and 7."
-msgstr ""
+msgstr "Die Nummer des Wochentages %f ist nicht zwischen 1 und 7."
 
 #: src/data/data-out.c:571
 #, c-format
 msgid "Month number %f is not between 1 and 12."
-msgstr ""
+msgstr "Die Nummer des Monats %f  ist nicht zwischen 1 und 12."
 
 #: src/data/dataset-reader.c:54
 #, c-format
@@ -252,7 +252,7 @@ msgstr ""
 #. messages in fh_lock() that identify types of files.
 #: src/data/dataset-writer.c:66 src/language/data-io/file-handle.q:187
 msgid "dataset"
-msgstr ""
+msgstr "Datensatz"
 
 #: src/data/dict-class.c:52
 msgid "ordinary"
@@ -277,7 +277,7 @@ msgstr ""
 
 #: src/data/file-handle-def.c:254
 msgid "active dataset"
-msgstr ""
+msgstr "aktiver Datensatz"
 
 #: src/data/file-handle-def.c:465
 #, c-format
@@ -541,7 +541,7 @@ msgstr ""
 
 #: src/data/por-file-reader.c:386
 msgid "Number expected."
-msgstr ""
+msgstr "Zahl erwartet."
 
 #: src/data/por-file-reader.c:414
 msgid "Missing numeric terminator."
@@ -743,7 +743,7 @@ msgstr ""
 #: src/data/sys-file-reader.c:467
 #, c-format
 msgid "Weighting variable must be numeric (not string variable `%s')."
-msgstr ""
+msgstr "Gewichtungsvariable muss numerisch sein. ( nicht Stringvariable `%s')."
 
 #: src/data/sys-file-reader.c:502
 #, c-format
@@ -807,7 +807,7 @@ msgstr ""
 #: src/data/sys-file-reader.c:959
 #, c-format
 msgid "Invalid variable name `%s'."
-msgstr ""
+msgstr "Ungültiges Variablenlabel `%s'."
 
 #: src/data/sys-file-reader.c:967
 #, c-format
@@ -1053,7 +1053,7 @@ msgstr ""
 #: src/data/sys-file-reader.c:2541
 #, c-format
 msgid "System error: %s."
-msgstr ""
+msgstr "System Fehler: %s."
 
 #: src/data/sys-file-reader.c:2543
 msgid "Unexpected end of file."
@@ -1108,7 +1108,7 @@ msgstr "Zentriert"
 #: src/language/utilities/set.q:220
 #, c-format
 msgid "%s is not yet implemented."
-msgstr ""
+msgstr "%s ist noch nicht implementiert."
 
 #: src/language/command.c:201
 #, c-format
@@ -1251,7 +1251,7 @@ msgstr ""
 
 #: src/language/lexer/lexer.c:398
 msgid "Syntax error at end of input"
-msgstr ""
+msgstr "Syntax Fehler am ende der Eingabe"
 
 #: src/language/lexer/lexer.c:419 src/language/xforms/select-if.c:60
 #: src/language/stats/autorecode.c:206 src/language/stats/npar.c:478
@@ -1277,16 +1277,16 @@ msgstr ""
 
 #: src/language/lexer/lexer.c:1300
 msgid "Syntax error at end of command"
-msgstr ""
+msgstr "Syntax Fehler am Ende des Befehls"
 
 #: src/language/lexer/lexer.c:1309
 #, c-format
 msgid "Syntax error at `%s'"
-msgstr ""
+msgstr "Syntax Fehler bei `%s'"
 
 #: src/language/lexer/lexer.c:1312
 msgid "Syntax error"
-msgstr ""
+msgstr "Syntax Fehler"
 
 #: src/language/lexer/lexer.c:1476
 #, c-format
@@ -1319,7 +1319,7 @@ msgstr ""
 
 #: src/language/lexer/lexer.c:1508
 msgid "Unexpected `.' in middle of command"
-msgstr ""
+msgstr "Unerwartetes  `.' in der Mitte des Befehls."
 
 #: src/language/lexer/lexer.c:1514
 #, c-format
@@ -1355,7 +1355,7 @@ msgstr ""
 #: src/language/data-io/placement-parser.c:225
 #, c-format
 msgid "Unknown format type `%s'."
-msgstr ""
+msgstr "Unbekannter Formattyp  `%s'."
 
 #: src/language/lexer/format-parser.c:131
 msgid "expecting format type"
@@ -1390,17 +1390,17 @@ msgstr ""
 #: src/language/lexer/variable-parser.c:77
 #, c-format
 msgid "%s is not a variable name."
-msgstr ""
+msgstr "%s ist kein Variablenlabel"
 
 #: src/language/lexer/variable-parser.c:180
 #, c-format
 msgid "%s is not a numeric variable.  It will not be included in the variable list."
-msgstr ""
+msgstr "%s ist keine numerische Variable. Es wird nicht in die Variablenliste eingefügt."
 
 #: src/language/lexer/variable-parser.c:183
 #, c-format
 msgid "%s is not a string variable.  It will not be included in the variable list."
-msgstr ""
+msgstr "%s ist keine String-Variable. Es wird nicht in die Variablenliste eingefügt."
 
 #: src/language/lexer/variable-parser.c:187
 #, c-format
@@ -1421,7 +1421,7 @@ msgstr ""
 #: src/language/lexer/variable-parser.c:404
 #, c-format
 msgid "Variable %s appears twice in variable list."
-msgstr ""
+msgstr "Variable %s taucht zwei Mal in der Variablenliste auf."
 
 #: src/language/lexer/variable-parser.c:315
 #, c-format
@@ -1525,7 +1525,7 @@ msgstr ""
 
 #: src/language/xforms/select-if.c:115
 msgid "The filter variable must be numeric."
-msgstr ""
+msgstr "Die Filtervariable muss numerisch sein."
 
 #: src/language/xforms/select-if.c:121
 msgid "The filter variable may not be scratch."
@@ -1726,7 +1726,7 @@ msgstr "Variablen"
 
 #: src/language/dictionary/mrsets.c:550
 msgid "Details"
-msgstr ""
+msgstr "Details"
 
 #: src/language/dictionary/mrsets.c:564
 msgid "Multiple dichotomy set"
@@ -1781,7 +1781,7 @@ msgstr ""
 #: src/language/dictionary/numeric.c:87 src/language/dictionary/numeric.c:157
 #, c-format
 msgid "There is already a variable named %s."
-msgstr ""
+msgstr "Es existiert bereits eine Variable namens  %s."
 
 #: src/language/dictionary/numeric.c:142
 #, c-format
@@ -1819,7 +1819,7 @@ msgstr "Label:"
 
 #: src/language/dictionary/sys-file-info.c:100
 msgid "No label."
-msgstr ""
+msgstr "Kein Label."
 
 #: src/language/dictionary/sys-file-info.c:103
 msgid "Created:"
@@ -1890,7 +1890,7 @@ msgstr ""
 
 #: src/language/dictionary/sys-file-info.c:133
 msgid "Not weighted."
-msgstr ""
+msgstr "Nicht gewichtet."
 
 #: src/language/dictionary/sys-file-info.c:135
 msgid "Mode:"
@@ -1903,11 +1903,11 @@ msgstr ""
 
 #: src/language/dictionary/sys-file-info.c:137
 msgid "on"
-msgstr ""
+msgstr "an"
 
 #: src/language/dictionary/sys-file-info.c:137
 msgid "off"
-msgstr ""
+msgstr "aus"
 
 #: src/language/dictionary/sys-file-info.c:140
 msgid "Charset:"
@@ -1985,7 +1985,7 @@ msgstr ""
 
 #: src/language/dictionary/sys-file-info.c:525
 msgid "Missing Values: "
-msgstr ""
+msgstr "Fehlende Werte:"
 
 #: src/language/dictionary/sys-file-info.c:624
 msgid "No vectors defined."
@@ -2029,7 +2029,7 @@ msgstr ""
 #: src/language/dictionary/vector.c:171
 #, c-format
 msgid "%s is an existing variable name."
-msgstr ""
+msgstr "%s ist ein existierendes Variablenlabel"
 
 #: src/language/dictionary/variable-display.c:120
 msgid "Variable display width must be a positive integer."
@@ -2734,7 +2734,7 @@ msgstr ""
 
 #: src/language/stats/glm.c:167
 msgid "Multivariate analysis is not yet implemented"
-msgstr ""
+msgstr "Multivariate Analysen sind noch nicht implementiert"
 
 #: src/language/stats/glm.c:272
 msgid "Only types 1, 2 & 3 sums of squares are currently implemented"
@@ -2773,7 +2773,7 @@ msgstr ""
 
 #: src/language/stats/glm.c:844
 msgid "Error"
-msgstr ""
+msgstr "Fehler"
 
 #: src/language/stats/glm.c:860
 msgid "Corrected Total"
@@ -2920,7 +2920,7 @@ msgstr ""
 #: src/language/stats/means.c:1066 src/language/stats/crosstabs.q:839
 #: src/language/stats/examine.q:1178 src/language/stats/frequencies.q:823
 msgid "Percent"
-msgstr ""
+msgstr "Prozent"
 
 #: src/language/stats/means.c:1096 src/language/stats/means.c:1105
 #: src/language/stats/means.c:1114
@@ -3334,11 +3334,11 @@ msgstr ""
 
 #: src/language/stats/roc.c:1068
 msgid "Unweighted"
-msgstr ""
+msgstr "ungewichtet"
 
 #: src/language/stats/roc.c:1069
 msgid "Weighted"
-msgstr ""
+msgstr "gewichtet"
 
 #: src/language/stats/roc.c:1073
 msgid "Valid N (listwise)"
@@ -3800,19 +3800,19 @@ msgstr ""
 #: src/language/data-io/dataset.c:63
 #, c-format
 msgid "There is no dataset named %s."
-msgstr ""
+msgstr "Es gibt keinen Datensatz namens %s."
 
 #: src/language/data-io/dataset.c:257
 msgid "Dataset"
-msgstr ""
+msgstr "Datensatz"
 
 #: src/language/data-io/dataset.c:265
 msgid "unnamed dataset"
-msgstr ""
+msgstr "unbenannter Datensatz"
 
 #: src/language/data-io/dataset.c:269
 msgid "(active dataset)"
-msgstr ""
+msgstr "(aktiver Datensatz)"
 
 #: src/language/data-io/get-data.c:99
 #, c-format
@@ -4122,7 +4122,7 @@ msgstr ""
 
 #: src/libpspp/message.c:85
 msgid "error"
-msgstr ""
+msgstr "Fehler"
 
 #: src/libpspp/message.c:87
 msgid "warning"
@@ -4415,7 +4415,7 @@ msgstr ""
 #: src/output/charts/plot-hist-cairo.c:112
 #: src/language/stats/frequencies.q:822
 msgid "Frequency"
-msgstr ""
+msgstr "Häufigkeit"
 
 #: src/output/charts/roc-chart-cairo.c:36 src/ui/gui/roc.ui:7
 msgid "ROC Curve"
@@ -4651,7 +4651,7 @@ msgstr "Eine Software zur Analyse von statistischen Daten"
 #. translation to your language.
 #: src/ui/gui/help-menu.c:76
 msgid "translator-credits"
-msgstr "Matthias Keil"
+msgstr "Matthias KeilStefan Grotz"
 
 #: src/ui/gui/help-menu.c:106
 #, c-format
@@ -4816,7 +4816,7 @@ msgstr ""
 #: src/ui/gui/psppire-dialog-action-var-info.c:124
 #, c-format
 msgid "Missing Values: %s\n"
-msgstr ""
+msgstr "Fehlende Werte: %s\n"
 
 #: src/ui/gui/psppire-dialog-action-var-info.c:128
 #, c-format
@@ -4825,7 +4825,7 @@ msgstr ""
 
 #: src/ui/gui/psppire-dialog-action-var-info.c:141
 msgid "Value Labels:\n"
-msgstr ""
+msgstr "Wertelabel:\n"
 
 #: src/ui/gui/psppire-dialog-action-var-info.c:150
 #, c-format
@@ -5185,7 +5185,7 @@ msgstr ""
 #: src/ui/gui/text-data-import-dialog.c:453
 #, c-format
 msgid "Could not open `%s': %s"
-msgstr ""
+msgstr " `%s': %s konnte nicht geöffnet werden"
 
 #: src/ui/gui/text-data-import-dialog.c:469
 #, c-format
@@ -5320,12 +5320,12 @@ msgstr "Fälle gewichten mit %s"
 #: src/language/utilities/set.q:156 src/language/utilities/set.q:163
 #, c-format
 msgid "%s must be at least 1."
-msgstr ""
+msgstr "%s muss mindestens 1 sein."
 
 #: src/language/utilities/set.q:170 src/language/data-io/file-handle.q:102
 #, c-format
 msgid "%s must not be negative."
-msgstr ""
+msgstr "%s darf nicht negativ sein."
 
 #: src/language/utilities/set.q:189
 msgid "WORKSPACE must be at least 1MB"
@@ -5414,7 +5414,7 @@ msgstr ""
 #: src/language/utilities/set.q:870
 #, c-format
 msgid "%s is %s."
-msgstr ""
+msgstr "%s ist %s."
 
 #: src/language/utilities/set.q:973
 #, c-format
@@ -5444,7 +5444,7 @@ msgstr ""
 
 #: src/language/stats/crosstabs.q:826
 msgid "Summary."
-msgstr ""
+msgstr "Zusammenfassung."
 
 #. TRANSLATORS: The %s here describes a crosstabulation.  It takes the
 #. form "var1 * var2 * var3 * ...".
@@ -5686,7 +5686,7 @@ msgstr ""
 
 #: src/language/stats/frequencies.q:381
 msgid "Bar charts are not implemented."
-msgstr ""
+msgstr "Balkendiagramme sind noch nicht implementiert."
 
 #: src/language/stats/frequencies.q:398
 #, c-format
@@ -5710,15 +5710,15 @@ msgstr ""
 
 #: src/language/stats/frequencies.q:820
 msgid "Value Label"
-msgstr ""
+msgstr "Wertelabel"
 
 #: src/language/stats/frequencies.q:824
 msgid "Valid Percent"
-msgstr ""
+msgstr "Gültige Prozente"
 
 #: src/language/stats/frequencies.q:825
 msgid "Cum Percent"
-msgstr ""
+msgstr "Kumulierte Prozente"
 
 #: src/language/stats/frequencies.q:1015
 #, c-format
@@ -5789,7 +5789,7 @@ msgstr ""
 
 #: src/language/stats/regression.q:938
 msgid "REGRESSION requires numeric variables."
-msgstr ""
+msgstr "REGRESSION benötigt numerische Variablen."
 
 #: src/language/stats/regression.q:1013
 msgid "No valid data found. This command was skipped."
@@ -7221,7 +7221,7 @@ msgstr "Anzahl Dezimalstellen:"
 
 #: src/ui/gui/var-sheet-dialogs.ui:419 src/ui/gui/var-sheet-dialogs.ui:610
 msgid "Value Labels"
-msgstr "Wertelabels"
+msgstr "Wertelabel"
 
 #: src/ui/gui/var-sheet-dialogs.ui:516
 msgid "Value Label:"
@@ -7332,7 +7332,7 @@ msgstr "Gehe zu Fall..."
 
 #: src/ui/gui/data-editor.ui:140
 msgid "Jump to a case in the data sheet"
-msgstr ""
+msgstr "Gehe zu Fall"
 
 #: src/ui/gui/data-editor.ui:166
 msgid "Cl_ear Variables"
@@ -7348,7 +7348,7 @@ msgstr ""
 
 #: src/ui/gui/data-editor.ui:176
 msgid "Delete the cases at the selected position(s)"
-msgstr ""
+msgstr "Lösche Fälle in den/der ausgewählten Position(en)"
 
 #: src/ui/gui/data-editor.ui:183
 msgid "_Find..."
@@ -7408,7 +7408,7 @@ msgstr "Datei aufte_ilen..."
 
 #: src/ui/gui/data-editor.ui:268
 msgid "Split the active dataset"
-msgstr ""
+msgstr "Datei aufteilen"
 
 #: src/ui/gui/data-editor.ui:275
 msgid "Select _Cases..."
@@ -7560,7 +7560,7 @@ msgstr "_Variablen..."
 
 #: src/ui/gui/data-editor.ui:502
 msgid "Jump to variable"
-msgstr ""
+msgstr "Zu Variable springen"
 
 #: src/ui/gui/data-editor.ui:509
 msgid "Data File _Comments..."
index d262e2ebc3a8438ab56404928361bd19b54eeb5b..c4ae97c285928ed6e701cc6594fe0fefed5824c7 100644 (file)
@@ -23,6 +23,7 @@
 
 #include "libpspp/array.h"
 #include "libpspp/hash-functions.h"
+#include "libpspp/i18n.h"
 
 #include "gl/xalloc.h"
 
@@ -223,8 +224,8 @@ attrset_lookup (struct attrset *set, const char *name)
 {
   struct attribute *attr;
   HMAP_FOR_EACH_WITH_HASH (attr, struct attribute, node,
-                           hash_case_string (name, 0), &set->map)
-    if (!strcasecmp (attribute_get_name (attr), name))
+                           utf8_hash_case_string (name, 0), &set->map)
+    if (!utf8_strcasecmp (attribute_get_name (attr), name))
       break;
   return attr;
 }
@@ -237,7 +238,7 @@ attrset_add (struct attrset *set, struct attribute *attr)
 {
   const char *name = attribute_get_name (attr);
   assert (attrset_lookup (set, name) == NULL);
-  hmap_insert (&set->map, &attr->node, hash_case_string (name, 0));
+  hmap_insert (&set->map, &attr->node, utf8_hash_case_string (name, 0));
 }
 
 /* Deletes any attribute from SET that matches NAME
index cbc617b3d2a2634e77fe35ac1d02be2039308591..4ebff0bd001288e28ddf6d085cbc086632b72e88 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 2010, 2011, 2012 Free Software Foundation, Inc.
 
    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
@@ -281,6 +281,7 @@ csv_write_var__ (struct csv_writer *w, const struct csv_var *cv,
   else
     {
       char s[MAX (DBL_STRLEN_BOUND, 128)];
+      char *cp;
 
       switch (cv->format.type)
         {
@@ -307,12 +308,9 @@ csv_write_var__ (struct csv_writer *w, const struct csv_var *cv,
         case FMT_WKDAY:
         case FMT_MONTH:
           dtoastr (s, sizeof s, 0, 0, value->f);
-          if (w->opts.decimal != '.')
-            {
-              char *cp = strchr (s, '.');
-              if (cp != NULL)
-                *cp = w->opts.decimal;
-            }
+          cp = strpbrk (s, ".,");
+          if (cp != NULL)
+            *cp = w->opts.decimal;
           break;
 
         case FMT_DATE:
index 5d4496cf9085de0d54509f224a89e0b0261935d1..db99aa4fb896d9d8f99657b2e7c6caf41940948e 100644 (file)
@@ -1044,7 +1044,7 @@ parse_minute_second (struct data_in *i, double *time)
     *cp++ = ss_get_byte (&i->input);
   *cp = '\0';
 
-  *time += strtod (buf, NULL);
+  *time += c_strtod (buf, NULL);
 
   return NULL;
 }
index 94e72f668be641d81bedca51660e8716e5773c2b..d3125955b856da23edf85deeff9d48bf1a7556de 100644 (file)
@@ -41,6 +41,7 @@
 #include "libpspp/str.h"
 
 #include "gl/minmax.h"
+#include "gl/c-snprintf.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -244,7 +245,7 @@ output_N (const union value *input, const struct fmt_spec *format,
       char buf[128];
       number = fabs (round (number));
       if (number < power10 (format->w)
-          && sprintf (buf, "%0*.0f", format->w, number) == format->w)
+          && c_snprintf (buf, 128, "%0*.0f", format->w, number) == format->w)
         memcpy (output, buf, format->w);
       else
         output_overflow (format, output);
@@ -263,7 +264,7 @@ output_Z (const union value *input, const struct fmt_spec *format,
   if (input->f == SYSMIS)
     output_missing (format, output);
   else if (fabs (number) < power10 (format->w)
-           && sprintf (buf, "%0*.0f", format->w,
+           && c_snprintf (buf, 128, "%0*.0f", format->w,
                        fabs (round (number))) == format->w)
     {
       if (number < 0 && strspn (buf, "0") < format->w)
@@ -466,14 +467,14 @@ output_date (const union value *input, const struct fmt_spec *format,
           if (number < 0)
             *p++ = '-';
           number = fabs (number);
-          p += sprintf (p, "%*.0f", count, floor (number / 60. / 60. / 24.));
+          p += c_snprintf (p, 64, "%*.0f", count, floor (number / 60. / 60. / 24.));
           number = fmod (number, 60. * 60. * 24.);
           break;
         case 'H':
           if (number < 0)
             *p++ = '-';
           number = fabs (number);
-          p += sprintf (p, "%0*.0f", count, floor (number / 60. / 60.));
+          p += c_snprintf (p, 64, "%0*.0f", count, floor (number / 60. / 60.));
           number = fmod (number, 60. * 60.);
           break;
         case 'M':
@@ -489,7 +490,7 @@ output_date (const union value *input, const struct fmt_spec *format,
             {
               int d = MIN (format->d, excess_width - 4);
               int w = d + 3;
-              sprintf (p, ":%0*.*f", w, d, number);
+              c_snprintf (p, 64, ":%0*.*f", w, d, number);
              if (settings_get_decimal_char (FMT_F) != '.')
                 {
                   char *cp = strchr (p, '.');
@@ -742,7 +743,7 @@ output_scientific (double number, const struct fmt_spec *format,
   /* Figure out number of characters we can use for the fraction,
      if any.  (If that turns out to be 1, then we'll output a
      decimal point without any digits following; that's what the
-     # flag does in the call to sprintf, below.) */
+     # flag does in the call to c_snprintf, below.) */
   fraction_width = MIN (MIN (format->d + 1, format->w - width), 16);
   if (format->type != FMT_E && fraction_width == 1)
     fraction_width = 0;
@@ -757,9 +758,9 @@ output_scientific (double number, const struct fmt_spec *format,
   if (add_affixes)
     p = stpcpy (p, style->prefix.s);
   if (fraction_width > 0)
-    sprintf (p, "%#.*E", fraction_width - 1, fabs (number));
+    c_snprintf (p, 64, "%#.*E", fraction_width - 1, fabs (number));
   else
-    sprintf (p, "%.0E", fabs (number));
+    c_snprintf (p, 64, "%.0E", fabs (number));
 
   /* The C locale always uses a period `.' as a decimal point.
      Translate to comma if necessary. */
@@ -819,7 +820,7 @@ rounder_init (struct rounder *r, double number, int max_decimals)
 
          We append ".00" to the integer representation because
          round_up assumes that fractional digits are present.  */
-      sprintf (r->string, "%.0f.00", fabs (round (number)));
+      c_snprintf (r->string, 64, "%.0f.00", fabs (round (number)));
     }
   else
     {
@@ -846,7 +847,7 @@ rounder_init (struct rounder *r, double number, int max_decimals)
          numbers does not hint how to do what we want, and it's
          not obvious how to change their algorithms to do so.  It
          would also be a lot of work. */
-      sprintf (r->string, "%.*f", max_decimals + 2, fabs (number));
+      c_snprintf (r->string, 64, "%.*f", max_decimals + 2, fabs (number));
       if (!strcmp (r->string + strlen (r->string) - 2, "50"))
         {
           int binary_exponent, decimal_exponent, format_decimals;
@@ -854,7 +855,7 @@ rounder_init (struct rounder *r, double number, int max_decimals)
           decimal_exponent = binary_exponent * 3 / 10;
           format_decimals = (DBL_DIG + 1) - decimal_exponent;
           if (format_decimals > max_decimals + 2)
-            sprintf (r->string, "%.*f", format_decimals, fabs (number));
+            c_snprintf (r->string, 64, "%.*f", format_decimals, fabs (number));
         }
     }
 
@@ -1090,7 +1091,7 @@ output_bcd_integer (double number, int digits, char *output)
   if (number != SYSMIS
       && number >= 0.
       && number < power10 (digits)
-      && sprintf (decimal, "%0*.0f", digits, round (number)) == digits)
+      && c_snprintf (decimal, 64, "%0*.0f", digits, round (number)) == digits)
     {
       const char *src = decimal;
       int i;
index 5731d78695148ad14e199d51ea0431109769f65e..d36f8c519f5f31437be7f70418f78ae99ce8ed9a 100644 (file)
@@ -397,7 +397,7 @@ add_var (struct dictionary *d, struct variable *v)
   vardict->dict = d;
   vardict->var = v;
   hmap_insert (&d->name_map, &vardict->name_node,
-               hash_case_string (var_get_name (v), 0));
+               utf8_hash_case_string (var_get_name (v), 0));
   vardict->case_index = d->next_value_idx;
   var_set_vardict (v, vardict);
 
@@ -487,10 +487,10 @@ dict_lookup_var (const struct dictionary *d, const char *name)
   struct vardict_info *vardict;
 
   HMAP_FOR_EACH_WITH_HASH (vardict, struct vardict_info, name_node,
-                           hash_case_string (name, 0), &d->name_map)
+                           utf8_hash_case_string (name, 0), &d->name_map)
     {
       struct variable *var = vardict->var;
-      if (!strcasecmp (var_get_name (var), name))
+      if (!utf8_strcasecmp (var_get_name (var), name))
         return var;
     }
 
@@ -742,7 +742,7 @@ rename_var (struct variable *v, const char *new_name)
   struct vardict_info *vardict = var_get_vardict (v);
   var_clear_vardict (v);
   var_set_name (v, new_name);
-  vardict->name_node.hash = hash_case_string (new_name, 0);
+  vardict->name_node.hash = utf8_hash_case_string (new_name, 0);
   var_set_vardict (v, vardict);
 }
 
@@ -753,7 +753,7 @@ void
 dict_rename_var (struct dictionary *d, struct variable *v,
                  const char *new_name)
 {
-  assert (!strcasecmp (var_get_name (v), new_name)
+  assert (!utf8_strcasecmp (var_get_name (v), new_name)
           || dict_lookup_var (d, new_name) == NULL);
 
   unindex_var (d, var_get_vardict (v));
@@ -1411,7 +1411,7 @@ dict_lookup_vector (const struct dictionary *d, const char *name)
 {
   size_t i;
   for (i = 0; i < d->vector_cnt; i++)
-    if (!strcasecmp (vector_get_name (d->vector[i]), name))
+    if (!utf8_strcasecmp (vector_get_name (d->vector[i]), name))
       return d->vector[i];
   return NULL;
 }
@@ -1456,7 +1456,7 @@ dict_lookup_mrset_idx (const struct dictionary *dict, const char *name)
   size_t i;
 
   for (i = 0; i < dict->n_mrsets; i++)
-    if (!strcasecmp (name, dict->mrsets[i]->name))
+    if (!utf8_strcasecmp (name, dict->mrsets[i]->name))
       return i;
 
   return SIZE_MAX;
index e4447a080b10dab9990c358f9b72315a8e9fc82b..121a4909c43cf6793acf4c09dc6643ccdd3a209f 100644 (file)
@@ -176,8 +176,8 @@ fh_from_id (const char *id)
   struct file_handle *handle;
 
   HMAP_FOR_EACH_WITH_HASH (handle, struct file_handle, name_node,
-                           hash_case_string (id, 0), &named_handles)
-    if (!strcasecmp (id, handle->id))
+                           utf8_hash_case_string (id, 0), &named_handles)
+    if (!utf8_strcasecmp (id, handle->id))
       {
        return fh_ref (handle);
       }
@@ -206,7 +206,7 @@ create_handle (const char *id, char *handle_name, enum fh_referent referent,
   if (id != NULL)
     {
       hmap_insert (&named_handles, &handle->name_node,
-                   hash_case_string (handle->id, 0));
+                   utf8_hash_case_string (handle->id, 0));
     }
 
   return handle;
index 58e16d3075a4249080db940efcb1e7dd7285c938..21162a05ae4f4ab21aff8012def2fdcd83d30123 100644 (file)
@@ -33,6 +33,7 @@
 #include "libpspp/misc.h"
 #include "libpspp/str.h"
 
+#include "gl/c-strcase.h"
 #include "gl/minmax.h"
 #include "gl/xalloc.h"
 
@@ -581,7 +582,7 @@ fmt_from_name (const char *name, enum fmt_type *type)
   int i;
 
   for (i = 0; i < FMT_NUMBER_OF_FORMATS; i++)
-    if (!strcasecmp (name, get_fmt_desc (i)->name))
+    if (!c_strcasecmp (name, get_fmt_desc (i)->name))
       {
         *type = i;
         return true;
index b9def31cb6d7ee9c9b728cb1a76125f08716ec18..a1a7415ca1b5f03097432324567293e9cfb549b4 100644 (file)
@@ -20,6 +20,7 @@
 #include "libpspp/misc.h"
 
 #include "gl/minmax.h"
+#include "gl/c-strtod.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -257,7 +258,7 @@ convert_xml_string_to_value (struct ccase *c, const struct variable *var,
       char *endptr;
 
       errno = 0;
-      v->f = strtod (text, &endptr);
+      v->f = c_strtod (text, &endptr);
       if ( errno != 0 || endptr == text)
        v->f = SYSMIS;
     }
index 4cbd8409eae549cf6cd1118a001c698e46a88125..630a720711ce14af83784137085cd8cb72f57ab2 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
 
    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
@@ -32,8 +32,9 @@
 #include "libpspp/misc.h"
 #include "libpspp/str.h"
 
-#include "gl/xalloc.h"
+#include "gl/c-strcase.h"
 #include "gl/minmax.h"
+#include "gl/xalloc.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -270,7 +271,7 @@ psql_open_reader (struct psql_read_info *info, struct dictionary **dict)
   {
     const char *dt =  PQparameterStatus (r->conn, "integer_datetimes");
 
-    r->integer_datetimes = ( 0 == strcasecmp (dt, "on"));
+    r->integer_datetimes = ( 0 == c_strcasecmp (dt, "on"));
   }
 
 #if USE_SSL
index a308ec1f624befa7c8e302c656944274bd82cc0d..c6f5031a5379e79a33cd84e6428b3d8ed2eeca1a 100644 (file)
@@ -25,6 +25,7 @@
 #include "libpspp/assertion.h"
 #include "libpspp/cast.h"
 #include "libpspp/hash-functions.h"
+#include "libpspp/i18n.h"
 #include "libpspp/str.h"
 #include "libpspp/hmapx.h"
 
@@ -94,7 +95,8 @@ session_add_dataset (struct session *s, struct dataset *ds)
   if (old != NULL)
     session_remove_dataset (s, old);
 
-  hmapx_insert (&s->datasets, ds, hash_case_string (dataset_name (ds), 0));
+  hmapx_insert (&s->datasets, ds,
+                utf8_hash_case_string (dataset_name (ds), 0));
   if (s->active == NULL)
     s->active = ds;
 
@@ -193,8 +195,9 @@ session_lookup_dataset__ (const struct session *s_, const char *name)
   struct hmapx_node *node;
   struct dataset *ds;
 
-  HMAPX_FOR_EACH_WITH_HASH (ds, node, hash_case_string (name, 0), &s->datasets)
-    if (!strcasecmp (dataset_name (ds), name))
+  HMAPX_FOR_EACH_WITH_HASH (ds, node, utf8_hash_case_string (name, 0),
+                            &s->datasets)
+    if (!utf8_strcasecmp (dataset_name (ds), name))
       return node;
 
   return NULL;
index 4a7476ae04840dfadf31103bde26e0f3bf1edd7a..35f40d3dbc4755f3365b8549fd2966f121f1f259 100644 (file)
@@ -50,6 +50,7 @@
 #include "libpspp/str.h"
 #include "libpspp/stringi-set.h"
 
+#include "gl/c-strtod.h"
 #include "gl/c-ctype.h"
 #include "gl/inttostr.h"
 #include "gl/localcharset.h"
@@ -1437,7 +1438,7 @@ parse_mrsets (struct sfm_reader *r, const struct sfm_extension_record *record,
           mrset->width = width;
           value_init (&mrset->counted, width);
           if (width == 0)
-            mrset->counted.f = strtod (counted, NULL);
+            mrset->counted.f = c_strtod (counted, NULL);
           else
             value_copy_str_rpad (&mrset->counted, width,
                                  (const uint8_t *) counted, ' ');
@@ -1572,7 +1573,8 @@ parse_long_var_name_map (struct sfm_reader *r,
 
   if (record == NULL)
     {
-      /* Convert variable names to lowercase. */
+      /* There are no long variable names.  Use the short variable names,
+         converted to lowercase, as the long variable names. */
       size_t i;
 
       for (i = 0; i < dict_get_var_cnt (dict); i++)
@@ -1580,11 +1582,8 @@ parse_long_var_name_map (struct sfm_reader *r,
          struct variable *var = dict_get_var (dict, i);
           char *new_name;
 
-          new_name = xstrdup (var_get_name (var));
-         str_lowercase (new_name);
-
+          new_name = utf8_to_lower (var_get_name (var));
           rename_var_and_save_short_names (dict, var, new_name);
-
           free (new_name);
        }
 
@@ -1609,7 +1608,7 @@ parse_long_var_name_map (struct sfm_reader *r,
         }
 
       /* Identify any duplicates. */
-      if (strcasecmp (var_get_short_name (var, 0), long_name)
+      if (utf8_strcasecmp (var_get_short_name (var, 0), long_name)
           && dict_lookup_var (dict, long_name) != NULL)
         {
           sys_warn (r, record->pos,
index 5003ca2b89bf1d1b4a4fbf3b98efdec02040f8b1..d02369856f356a08b33e3a9e5f4a7ac8d21eb5c6 100644 (file)
@@ -774,11 +774,12 @@ write_mrsets (struct sfm_writer *w, const struct dictionary *dict,
       for (j = 0; j < mrset->n_vars; j++)
         {
           const char *short_name_utf8 = var_get_short_name (mrset->vars[j], 0);
+          char *lower_name_utf8 = utf8_to_lower (short_name_utf8);
           char *short_name = recode_string (encoding, "UTF-8",
-                                            short_name_utf8, -1);
-          str_lowercase (short_name);
+                                            lower_name_utf8, -1);
           ds_put_format (&s, " %s", short_name);
           free (short_name);
+          free (lower_name_utf8);
         }
       ds_put_byte (&s, '\n');
     }
index 144f39b5241d1fb7b8b36021e57c23cea65a22ae..ce80076235f8c730a3be760f4931b6a19e0c05ae 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2009, 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
 
    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
@@ -173,6 +173,21 @@ value_resize (union value *value, int old_width, int new_width)
     }
 }
 
+/* Returns true if VALUE, with the given WIDTH, is all spaces, false otherwise.
+   Returns false if VALUE is numeric. */
+bool
+value_is_spaces (const union value *value, int width)
+{
+  const uint8_t *s = value_str (value, width);
+  int i;
+
+  for (i = 0; i < width; i++)
+    if (s[i] != ' ')
+      return false;
+
+  return true;
+}
+
 /* Returns true if resizing a value from OLD_WIDTH to NEW_WIDTH
    actually changes anything, false otherwise.  If false is
    returned, calls to value_resize() with the specified
index 9205bc1a031feec0bee4d6858ca5af3b2e3d0b2e..b1b27ed16eae86b938a99e5db3b2f45d89bafb36 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007, 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2012 Free Software Foundation, Inc.
 
    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
@@ -77,6 +77,8 @@ bool value_is_resizable (const union value *, int old_width, int new_width);
 bool value_needs_resize (int old_width, int new_width);
 void value_resize (union value *, int old_width, int new_width);
 
+bool value_is_spaces (const union value *, int width);
+
 static inline void value_swap (union value *, union value *);
 
 struct pool;
index 72d9ade6759cfd33e1ed855e5deac987fd18d3f5..fe4645ee41e1b7d7fad9042e5dc5014076ace03c 100644 (file)
@@ -201,7 +201,7 @@ compare_vars_by_name (const void *a_, const void *b_, const void *aux UNUSED)
   const struct variable *a = a_;
   const struct variable *b = b_;
 
-  return strcasecmp (a->name, b->name);
+  return utf8_strcasecmp (a->name, b->name);
 }
 
 /* A hsh_hash_func that hashes variable V based on its name. */
@@ -210,7 +210,7 @@ hash_var_by_name (const void *v_, const void *aux UNUSED)
 {
   const struct variable *v = v_;
 
-  return hash_case_string (v->name, 0);
+  return utf8_hash_case_string (v->name, 0);
 }
 
 /* A hsh_compare_func that orders pointers to variables A and B
@@ -222,7 +222,7 @@ compare_var_ptrs_by_name (const void *a_, const void *b_,
   struct variable *const *a = a_;
   struct variable *const *b = b_;
 
-  return strcasecmp (var_get_name (*a), var_get_name (*b));
+  return utf8_strcasecmp (var_get_name (*a), var_get_name (*b));
 }
 
 /* A hsh_compare_func that orders pointers to variables A and B
@@ -246,7 +246,7 @@ hash_var_ptr_by_name (const void *v_, const void *aux UNUSED)
 {
   struct variable *const *v = v_;
 
-  return hash_case_string (var_get_name (*v), 0);
+  return utf8_hash_case_string (var_get_name (*v), 0);
 }
 \f
 /* Returns the type of variable V. */
@@ -926,8 +926,7 @@ var_set_short_name (struct variable *var, size_t idx, const char *short_name)
           for (i = old_cnt; i < var->short_name_cnt; i++)
             var->short_names[i] = NULL;
         }
-      var->short_names[idx] = xstrdup (short_name);
-      str_uppercase (var->short_names[idx]);
+      var->short_names[idx] = utf8_to_upper (short_name);
     }
 
   dict_var_changed (var);
index 87046ad42b8127853f7a67b75bb2f07dc4583121..7c8ec4178dc9200473f308b385d35367441adac0 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2006, 2010, 2011  Free Software Foundation, Inc.
+   Copyright (C) 2006, 2010, 2011, 2012  Free Software Foundation, Inc.
 
    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
@@ -23,6 +23,7 @@
 #include "data/dictionary.h"
 #include "data/identifier.h"
 #include "libpspp/assertion.h"
+#include "libpspp/i18n.h"
 #include "libpspp/str.h"
 
 #include "gl/xalloc.h"
@@ -140,6 +141,6 @@ compare_vector_ptrs_by_name (const void *a_, const void *b_)
   struct vector *a = *pa;
   struct vector *b = *pb;
 
-  return strcasecmp (a->name, b->name);
+  return utf8_strcasecmp (a->name, b->name);
 }
 
index 72d45c2c2ff96344cfe65c3948475fd661792469..b2c2bb413e501bda41721c95b8ded972fd02950b 100644 (file)
 #include "libpspp/cast.h"
 #include "libpspp/hash-functions.h"
 #include "libpspp/hmap.h"
+#include "libpspp/i18n.h"
 #include "libpspp/message.h"
 #include "libpspp/str.h"
+#include "libpspp/misc.h"
 
 #include "gl/ftoastr.h"
 #include "gl/minmax.h"
@@ -77,7 +79,7 @@ cmd_do_repeat (struct lexer *lexer, struct dataset *ds)
 static unsigned int
 hash_dummy (const char *name, size_t name_len)
 {
-  return hash_case_bytes (name, name_len, 0);
+  return utf8_hash_case_bytes (name, name_len, 0);
 }
 
 static const struct dummy_var *
@@ -87,7 +89,7 @@ find_dummy_var (struct hmap *hmap, const char *name, size_t name_len)
 
   HMAP_FOR_EACH_WITH_HASH (dv, struct dummy_var, hmap_node,
                            hash_dummy (name, name_len), hmap)
-    if (strcasecmp (dv->name, name))
+    if (utf8_strcasecmp (dv->name, name))
       return dv;
 
   return NULL;
@@ -399,7 +401,7 @@ parse_numbers (struct lexer *lexer, struct dummy_var *dv)
         {
           char s[DBL_BUFSIZE_BOUND];
 
-          dtoastr (s, sizeof s, 0, 0, lex_number (lexer));
+          c_dtoastr (s, sizeof s, 0, 0, lex_number (lexer));
           add_replacement (dv, xstrdup (s), &allocated);
           lex_get (lexer);
         }
index 21736da8c645239dec3dadcde4cae59765d0f712..b7ba87ab7ccd39fd445121deea9d63eb1b4b4473 100644 (file)
@@ -69,6 +69,7 @@ struct comb_file
     /* Variables. */
     struct subcase by_vars;     /* BY variables in this input file. */
     struct subcase src, dst;    /* Data to copy to output; where to put it. */
+    const struct missing_values **mv; /* Each variable's missing values. */
 
     /* Input files. */
     struct file_handle *handle; /* Input file handle. */
@@ -197,6 +198,7 @@ combine_files (enum comb_command_type command,
       subcase_init_empty (&file->by_vars);
       subcase_init_empty (&file->src);
       subcase_init_empty (&file->dst);
+      file->mv = NULL;
       file->handle = NULL;
       file->dict = NULL;
       file->reader = NULL;
@@ -413,6 +415,7 @@ combine_files (enum comb_command_type command,
       size_t src_var_cnt = dict_get_var_cnt (file->dict);
       size_t j;
 
+      file->mv = xnmalloc (src_var_cnt, sizeof *file->mv);
       for (j = 0; j < src_var_cnt; j++)
         {
           struct variable *src_var = dict_get_var (file->dict, j);
@@ -420,6 +423,8 @@ combine_files (enum comb_command_type command,
                                                       var_get_name (src_var));
           if (dst_var != NULL)
             {
+              size_t n = subcase_get_n_fields (&file->src);
+              file->mv[n] = var_get_missing_values (src_var);
               subcase_add_var (&file->src, src_var, SC_ASCEND);
               subcase_add_var (&file->dst, dst_var, SC_ASCEND);
             }
@@ -633,6 +638,7 @@ close_all_comb_files (struct comb_proc *proc)
       subcase_destroy (&file->by_vars);
       subcase_destroy (&file->src);
       subcase_destroy (&file->dst);
+      free (file->mv);
       fh_unref (file->handle);
       dict_destroy (file->dict);
       casereader_destroy (file->reader);
@@ -665,8 +671,8 @@ free_comb_proc (struct comb_proc *proc)
 static bool scan_table (struct comb_file *, union value by[]);
 static struct ccase *create_output_case (const struct comb_proc *);
 static void apply_case (const struct comb_file *, struct ccase *);
-static void apply_file_case_and_advance (struct comb_file *, struct ccase *,
-                                         union value by[]);
+static void apply_nonmissing_case (const struct comb_file *, struct ccase *);
+static void advance_file (struct comb_file *, union value by[]);
 static void output_case (struct comb_proc *, struct ccase *, union value by[]);
 static void output_buffered_case (struct comb_proc *);
 
@@ -686,7 +692,8 @@ execute_add_files (struct comb_proc *proc)
           while (file->is_minimal)
             {
               struct ccase *output = create_output_case (proc);
-              apply_file_case_and_advance (file, output, by);
+              apply_case (file, output);
+              advance_file (file, by);
               output_case (proc, output, by);
             }
         }
@@ -712,7 +719,10 @@ execute_match_files (struct comb_proc *proc)
           if (file->type == COMB_FILE)
             {
               if (file->is_minimal)
-                apply_file_case_and_advance (file, output, NULL);
+                {
+                  apply_case (file, output);
+                  advance_file (file, NULL);
+                }
             }
           else
             {
@@ -743,7 +753,8 @@ execute_update (struct comb_proc *proc)
       for (first = &proc->files[0]; ; first++)
         if (first->is_minimal)
           break;
-      apply_file_case_and_advance (first, output, by);
+      apply_case (first, output);
+      advance_file (first, by);
 
       /* Read additional cases and update the output case from
          them.  (Don't update the output case from any duplicate
@@ -752,7 +763,10 @@ execute_update (struct comb_proc *proc)
            file < &proc->files[proc->n_files]; file++)
         {
           while (file->is_minimal)
-            apply_file_case_and_advance (file, output, by);
+            {
+              apply_nonmissing_case (file, output);
+              advance_file (file, by);
+            }
         }
       casewriter_write (proc->output, output);
 
@@ -764,7 +778,8 @@ execute_update (struct comb_proc *proc)
           while (first->is_minimal)
             {
               output = create_output_case (proc);
-              apply_file_case_and_advance (first, output, by);
+              apply_case (first, output);
+              advance_file (first, by);
               casewriter_write (proc->output, output);
             }
         }
@@ -821,25 +836,53 @@ create_output_case (const struct comb_proc *proc)
   return output;
 }
 
+static void
+mark_file_used (const struct comb_file *file, struct ccase *output)
+{
+  if (file->in_var != NULL)
+    case_data_rw (output, file->in_var)->f = true;
+}
+
 /* Copies the data from FILE's case into output case OUTPUT.
    If FILE has an IN variable, then it is set to 1 in OUTPUT. */
 static void
 apply_case (const struct comb_file *file, struct ccase *output)
 {
   subcase_copy (&file->src, file->data, &file->dst, output);
-  if (file->in_var != NULL)
-    case_data_rw (output, file->in_var)->f = true;
+  mark_file_used (file, output);
+}
+
+/* Copies the data from FILE's case into output case OUTPUT,
+   skipping values that are missing or all spaces.
+
+   If FILE has an IN variable, then it is set to 1 in OUTPUT. */
+static void
+apply_nonmissing_case (const struct comb_file *file, struct ccase *output)
+{
+  size_t i;
+
+  for (i = 0; i < subcase_get_n_fields (&file->src); i++)
+    {
+      const struct subcase_field *src_field = &file->src.fields[i];
+      const struct subcase_field *dst_field = &file->dst.fields[i];
+      const union value *src_value
+        = case_data_idx (file->data, src_field->case_index);
+      int width = src_field->width;
+
+      if (!mv_is_value_missing (file->mv[i], src_value, MV_ANY)
+          && !(width > 0 && value_is_spaces (src_value, width)))
+        value_copy (case_data_rw_idx (output, dst_field->case_index),
+                    src_value, width);
+    }
+  mark_file_used (file, output);
 }
 
-/* Like apply_case() above, but also advances FILE to its next
-   case.  Also, if BY is nonnull, then FILE's is_minimal member
-   is updated based on whether the new case's BY values still
-   match those in BY. */
+/* Advances FILE to its next case.  If BY is nonnull, then FILE's is_minimal
+   member is updated based on whether the new case's BY values still match
+   those in BY. */
 static void
-apply_file_case_and_advance (struct comb_file *file, struct ccase *output,
-                             union value by[])
+advance_file (struct comb_file *file, union value by[])
 {
-  apply_case (file, output);
   case_unref (file->data);
   file->data = casereader_read (file->reader);
   if (by)
index 3360e8d739dc46a2890dfb063fa225fd3b3e7c26..55e1b5d501360b6693de79cbdbd5b9349c380ba3 100644 (file)
@@ -27,6 +27,7 @@
 #include "libpspp/pool.h"
 #include "libpspp/str.h"
 
+#include "gl/c-strcase.h"
 #include "gl/xalloc.h"
 #include "gl/xsize.h"
 
@@ -52,6 +53,13 @@ static bool fixed_parse_fortran (struct lexer *l, struct pool *, enum fmt_use,
    formats like those parsed by DATA LIST or PRINT.  Returns true
    only if successful.
 
+   The formats parsed are either input or output formats, according
+   to USE.
+
+   If USE is FMT_FOR_INPUT, then T, X, and / "formats" are parsed,
+   in addition to regular formats.  If USE is FMT_FOR_OUTPUT, then
+   T and X "formats" are parsed but not /.
+
    If successful, formats for VAR_CNT variables are stored in
    *FORMATS, and the number of formats required is stored in
    *FORMAT_CNT.  *FORMAT_CNT may be greater than VAR_CNT because
@@ -204,7 +212,7 @@ fixed_parse_fortran (struct lexer *lexer, struct pool *pool, enum fmt_use use,
         {
           new_formats = &f;
           new_format_cnt = 1;
-          if (lex_match (lexer, T_SLASH))
+          if (use == FMT_FOR_INPUT && lex_match (lexer, T_SLASH))
             f.type = PRS_TYPE_NEW_REC;
           else
             {
@@ -213,9 +221,9 @@ fixed_parse_fortran (struct lexer *lexer, struct pool *pool, enum fmt_use use,
               if (!parse_abstract_format_specifier (lexer, type, &f.w, &f.d))
                 return false;
 
-              if (!strcasecmp (type, "T"))
+              if (!c_strcasecmp (type, "T"))
                 f.type = PRS_TYPE_T;
-              else if (!strcasecmp (type, "X"))
+              else if (!c_strcasecmp (type, "X"))
                 {
                   f.type = PRS_TYPE_X;
                   f.w = count;
@@ -291,11 +299,8 @@ execute_placement_format (const struct fmt_spec *format,
     }
 }
 
-/* Parses a BASE-based column using LEXER.  Returns true and
-   stores a 1-based column number into *COLUMN if successful,
-   otherwise emits an error message and returns false. */
-static bool
-parse_column (int value, int base, int *column)
+bool
+parse_column__ (int value, int base, int *column)
 {
   assert (base == 0 || base == 1);
   *column = value - base + 1;
@@ -310,6 +315,27 @@ parse_column (int value, int base, int *column)
   return true;
 }
 
+/* Parses a BASE-based column using LEXER.  Returns true and
+   stores a 1-based column number into *COLUMN if successful,
+   otherwise emits an error message and returns false.
+
+   If BASE is 0, zero-based column numbers are parsed; if BASE is
+   1, 1-based column numbers are parsed.  Regardless of BASE, the
+   values stored in *FIRST_COLUMN and *LAST_COLUMN are
+   1-based. */
+bool
+parse_column (struct lexer *lexer, int base, int *column)
+{
+  assert (base == 0 || base == 1);
+
+  if (!lex_force_int (lexer)
+      || !parse_column__ (lex_integer (lexer), base, column))
+    return false;
+
+  lex_get (lexer);
+  return true;
+}
+
 /* Parse a column or a range of columns, specified as a single
    integer or two integers delimited by a dash.  Stores the range
    in *FIRST_COLUMN and *LAST_COLUMN.  (If only a single integer
@@ -330,14 +356,14 @@ parse_column_range (struct lexer *lexer, int base,
 {
   /* First column. */
   if (!lex_force_int (lexer)
-      || !parse_column (lex_integer (lexer), base, first_column))
+      || !parse_column__ (lex_integer (lexer), base, first_column))
     return false;
   lex_get (lexer);
 
   /* Last column. */
   if (lex_is_integer (lexer) && lex_integer (lexer) < 0)
     {
-      if (!parse_column (-lex_integer (lexer), base, last_column))
+      if (!parse_column__ (-lex_integer (lexer), base, last_column))
         return false;
       lex_get (lexer);
 
index 962b97b4bcbfad0232a20a87819e69f710bf9ce2..8f8d68c4ce4f013b61ede2c3ce4e3174f153c895 100644 (file)
@@ -30,6 +30,7 @@ bool parse_var_placements (struct lexer *, struct pool *, size_t var_cnt,
                            struct fmt_spec **, size_t *format_cnt);
 bool execute_placement_format (const struct fmt_spec *,
                                int *record, int *column);
+bool parse_column (struct lexer *lexer, int base, int *column);
 bool parse_column_range (struct lexer *, int base,
                          int *first_column, int *last_column,
                          bool *range_specified);
index cffa3bd49fdd29f480798c311ba796ae791ff1da..e2c5adc073168c8a2eb74b6cad19c1318157463d 100644 (file)
@@ -17,6 +17,7 @@
 #include <config.h>
 
 #include <stdlib.h>
+#include <uniwidth.h>
 
 #include "data/case.h"
 #include "data/dataset.h"
@@ -38,6 +39,7 @@
 #include "libpspp/message.h"
 #include "libpspp/misc.h"
 #include "libpspp/pool.h"
+#include "libpspp/u8-line.h"
 #include "output/tab.h"
 #include "output/text-item.h"
 
@@ -70,6 +72,7 @@ struct prt_out_spec
 
     /* PRT_LITERAL only. */
     struct string string;       /* String to output. */
+    int width;                  /* Width of 'string', in display columns. */
   };
 
 static inline struct prt_out_spec *
@@ -88,7 +91,6 @@ struct print_trns
     struct dfm_writer *writer; /* Output file, NULL=listing file. */
     struct ll_list specs;       /* List of struct prt_out_specs. */
     size_t record_cnt;          /* Number of records to write. */
-    struct string line;         /* Output buffer. */
   };
 
 enum which_formats
@@ -99,7 +101,7 @@ enum which_formats
 
 static int internal_cmd_print (struct lexer *, struct dataset *ds,
                               enum which_formats, bool eject);
-static trns_proc_func print_trns_proc;
+static trns_proc_func print_text_trns_proc, print_binary_trns_proc;
 static trns_free_func print_trns_free;
 static bool parse_specs (struct lexer *, struct pool *tmp_pool, struct print_trns *,
                         struct dictionary *dict, enum which_formats);
@@ -133,11 +135,13 @@ static int
 internal_cmd_print (struct lexer *lexer, struct dataset *ds,
                    enum which_formats which_formats, bool eject)
 {
-  bool print_table = 0;
+  bool print_table = false;
+  const struct prt_out_spec *spec;
   struct print_trns *trns;
   struct file_handle *fh = NULL;
   char *encoding = NULL;
   struct pool *tmp_pool;
+  bool binary;
 
   /* Fill in prt to facilitate error-handling. */
   trns = pool_create_container (struct print_trns, pool);
@@ -145,8 +149,6 @@ internal_cmd_print (struct lexer *lexer, struct dataset *ds,
   trns->writer = NULL;
   trns->record_cnt = 0;
   ll_init (&trns->specs);
-  ds_init_empty (&trns->line);
-  ds_register_pool (&trns->line, trns->pool);
 
   tmp_pool = pool_create_subpool (trns->pool);
 
@@ -201,6 +203,27 @@ internal_cmd_print (struct lexer *lexer, struct dataset *ds,
   if (!parse_specs (lexer, tmp_pool, trns, dataset_dict (ds), which_formats))
     goto error;
 
+  /* Are there any binary formats?
+
+     There are real difficulties figuring out what to do when both binary
+     formats and nontrivial encodings enter the picture.  So when binary
+     formats are present we fall back to much simpler handling. */
+  binary = false;
+  ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
+    {
+      if (spec->type == PRT_VAR
+          && fmt_get_category (spec->format.type) == FMT_CAT_BINARY)
+        {
+          binary = true;
+          break;
+        }
+    }
+  if (binary && fh == NULL)
+    {
+      msg (SE, _("OUTFILE is required when binary formats are specified."));
+      goto error;
+    }
+
   if (lex_end_of_command (lexer) != CMD_SUCCESS)
     goto error;
 
@@ -219,7 +242,11 @@ internal_cmd_print (struct lexer *lexer, struct dataset *ds,
     dump_table (trns, fh);
 
   /* Put the transformation in the queue. */
-  add_transformation (ds, print_trns_proc, print_trns_free, trns);
+  add_transformation (ds,
+                      (binary
+                       ? print_binary_trns_proc
+                       : print_text_trns_proc),
+                      print_trns_free, trns);
 
   pool_destroy (tmp_pool);
   fh_unref (fh);
@@ -267,8 +294,8 @@ parse_specs (struct lexer *lexer, struct pool *tmp_pool, struct print_trns *trns
       if (lex_is_string (lexer))
        ok = parse_string_argument (lexer, trns, record, &column);
       else
-       ok = parse_variable_argument (lexer, dict, trns, tmp_pool, &record, &column,
-                                      which_formats);
+       ok = parse_variable_argument (lexer, dict, trns, tmp_pool, &record,
+                                      &column, which_formats);
       if (!ok)
        return 0;
 
@@ -310,7 +337,11 @@ parse_string_argument (struct lexer *lexer, struct print_trns *trns, int record,
       if (range_specified)
         ds_set_length (&spec->string, last_column - first_column + 1, ' ');
     }
-  *column = spec->first_column + ds_length (&spec->string);
+
+  spec->width = u8_strwidth (CHAR_CAST (const uint8_t *,
+                                        ds_cstr (&spec->string)),
+                             UTF8);
+  *column = spec->first_column + spec->width;
 
   ll_push_tail (&trns->specs, &spec->ll);
   return true;
@@ -455,53 +486,73 @@ dump_table (struct print_trns *trns, const struct file_handle *fh)
   tab_submit (t);
 }
 \f
-/* Transformation. */
+/* Transformation, for all-text output. */
 
-static void flush_records (struct print_trns *, int target_record,
-                           bool *eject, int *record);
+static void print_text_flush_records (struct print_trns *, struct u8_line *,
+                                      int target_record,
+                                      bool *eject, int *record);
 
 /* Performs the transformation inside print_trns T on case C. */
 static int
-print_trns_proc (void *trns_, struct ccase **c, casenumber case_num UNUSED)
+print_text_trns_proc (void *trns_, struct ccase **c,
+                      casenumber case_num UNUSED)
 {
   struct print_trns *trns = trns_;
+  struct prt_out_spec *spec;
+  struct u8_line line;
+
   bool eject = trns->eject;
-  char encoded_space = recode_byte (trns->encoding, C_ENCODING, ' ');
   int record = 1;
-  struct prt_out_spec *spec;
 
-  ds_clear (&trns->line);
-  ds_put_byte (&trns->line, ' ');
+  u8_line_init (&line);
   ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
     {
-      flush_records (trns, spec->record, &eject, &record);
+      int x0 = spec->first_column;
 
-      ds_set_length (&trns->line, spec->first_column, encoded_space);
+      print_text_flush_records (trns, &line, spec->record, &eject, &record);
+
+      u8_line_set_length (&line, spec->first_column);
       if (spec->type == PRT_VAR)
         {
           const union value *input = case_data (*c, spec->var);
+          int x1;
+
           if (!spec->sysmis_as_spaces || input->f != SYSMIS)
-            data_out_recode (input, var_get_encoding (spec->var),
-                             &spec->format, &trns->line, trns->encoding);
+            {
+              size_t len;
+              int width;
+              char *s;
+
+              s = data_out (input, var_get_encoding (spec->var),
+                            &spec->format);
+              len = strlen (s);
+              width = u8_width (CHAR_CAST (const uint8_t *, s), len, UTF8);
+              x1 = x0 + width;
+              u8_line_put (&line, x0, x1, s, len);
+              free (s);
+            }
           else
-            ds_put_byte_multiple (&trns->line, encoded_space, spec->format.w);
+            {
+              int n = spec->format.w;
+
+              x1 = x0 + n;
+              memset (u8_line_reserve (&line, x0, x1, n), ' ', n);
+            }
+
           if (spec->add_space)
-            ds_put_byte (&trns->line, encoded_space);
+            *u8_line_reserve (&line, x1, x1 + 1, 1) = ' ';
         }
       else
         {
-          ds_put_substring (&trns->line, ds_ss (&spec->string));
-          if (0 != strcmp (trns->encoding, C_ENCODING))
-            {
-              size_t length = ds_length (&spec->string);
-              char *data = ss_data (ds_tail (&trns->line, length));
-             char *s = recode_string (trns->encoding, C_ENCODING, data, length);
-             memcpy (data, s, length);
-             free (s);
-            }
+          const struct string *s = &spec->string;
+
+          u8_line_put (&line, x0, x0 + spec->width,
+                       ds_data (s), ds_length (s));
         }
     }
-  flush_records (trns, trns->record_cnt + 1, &eject, &record);
+  print_text_flush_records (trns, &line, trns->record_cnt + 1,
+                            &eject, &record);
+  u8_line_destroy (&line);
 
   if (trns->writer != NULL && dfm_write_error (trns->writer))
     return TRNS_ERROR;
@@ -513,13 +564,11 @@ print_trns_proc (void *trns_, struct ccase **c, casenumber case_num UNUSED)
    output is preceded by ejecting the page (and *EJECT is set
    false). */
 static void
-flush_records (struct print_trns *trns, int target_record,
-               bool *eject, int *record)
+print_text_flush_records (struct print_trns *trns, struct u8_line *line,
+                          int target_record, bool *eject, int *record)
 {
   for (; target_record > *record; (*record)++)
     {
-      char *line = ds_cstr (&trns->line);
-      size_t length = ds_length (&trns->line);
       char leader = ' ';
 
       if (*eject)
@@ -530,24 +579,123 @@ flush_records (struct print_trns *trns, int target_record,
           else
             leader = '1';
         }
-      line[0] = recode_byte (trns->encoding, C_ENCODING, leader);
+      *u8_line_reserve (line, 0, 1, 1) = leader;
 
       if (trns->writer == NULL)
-        tab_output_text (TAB_FIX, &line[1]);
+        tab_output_text (TAB_FIX, ds_cstr (&line->s) + 1);
       else
         {
+          size_t len = ds_length (&line->s);
+          char *s = ds_cstr (&line->s);
+
           if (!trns->include_prefix)
             {
-              line++;
-              length--;
+              s++;
+              len--;
+            }
+
+          if (is_encoding_utf8 (trns->encoding))
+            dfm_put_record (trns->writer, s, len);
+          else
+            {
+              char *recoded = recode_string (trns->encoding, UTF8, s, len);
+              dfm_put_record (trns->writer, recoded, strlen (recoded));
+              free (recoded);
             }
-          dfm_put_record (trns->writer, line, length);
         }
+    }
+}
+\f
+/* Transformation, for output involving binary. */
+
+static void print_binary_flush_records (struct print_trns *,
+                                        struct string *line, int target_record,
+                                        bool *eject, int *record);
+
+/* Performs the transformation inside print_trns T on case C. */
+static int
+print_binary_trns_proc (void *trns_, struct ccase **c,
+                        casenumber case_num UNUSED)
+{
+  struct print_trns *trns = trns_;
+  bool eject = trns->eject;
+  char encoded_space = recode_byte (trns->encoding, C_ENCODING, ' ');
+  int record = 1;
+  struct prt_out_spec *spec;
+  struct string line;
+
+  ds_init_empty (&line);
+  ds_put_byte (&line, ' ');
+  ll_for_each (spec, struct prt_out_spec, ll, &trns->specs)
+    {
+      print_binary_flush_records (trns, &line, spec->record, &eject, &record);
 
-      ds_truncate (&trns->line, 1);
+      ds_set_length (&line, spec->first_column, encoded_space);
+      if (spec->type == PRT_VAR)
+        {
+          const union value *input = case_data (*c, spec->var);
+          if (!spec->sysmis_as_spaces || input->f != SYSMIS)
+            data_out_recode (input, var_get_encoding (spec->var),
+                             &spec->format, &line, trns->encoding);
+          else
+            ds_put_byte_multiple (&line, encoded_space, spec->format.w);
+          if (spec->add_space)
+            ds_put_byte (&line, encoded_space);
+        }
+      else
+        {
+          ds_put_substring (&line, ds_ss (&spec->string));
+          if (0 != strcmp (trns->encoding, UTF8))
+            {
+              size_t length = ds_length (&spec->string);
+              char *data = ss_data (ds_tail (&line, length));
+             char *s = recode_string (trns->encoding, UTF8, data, length);
+             memcpy (data, s, length);
+             free (s);
+            }
+        }
     }
+  print_binary_flush_records (trns, &line, trns->record_cnt + 1,
+                              &eject, &record);
+  ds_destroy (&line);
+
+  if (trns->writer != NULL && dfm_write_error (trns->writer))
+    return TRNS_ERROR;
+  return TRNS_CONTINUE;
 }
 
+/* Advance from *RECORD to TARGET_RECORD, outputting records
+   along the way.  If *EJECT is true, then the first record
+   output is preceded by ejecting the page (and *EJECT is set
+   false). */
+static void
+print_binary_flush_records (struct print_trns *trns, struct string *line,
+                            int target_record, bool *eject, int *record)
+{
+  for (; target_record > *record; (*record)++)
+    {
+      char *s = ds_cstr (line);
+      size_t length = ds_length (line);
+      char leader = ' ';
+
+      if (*eject)
+        {
+          *eject = false;
+          leader = '1';
+        }
+      s[0] = recode_byte (trns->encoding, C_ENCODING, leader);
+
+      if (!trns->include_prefix)
+        {
+          s++;
+          length--;
+        }
+      dfm_put_record (trns->writer, s, length);
+
+      ds_truncate (line, 1);
+    }
+}
+\f
 /* Frees TRNS. */
 static bool
 print_trns_free (void *trns_)
index d73b95e8a2236b25f7172efb57c673e1f8d309c1..f0b03d48f9ee426e264a462ac48ca48c4ae2892c 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2010, 2011, 2012 Free Software Foundation, Inc.
 
    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
@@ -28,6 +28,7 @@
 #include "libpspp/assertion.h"
 #include "libpspp/bit-vector.h"
 #include "libpspp/compiler.h"
+#include "libpspp/i18n.h"
 #include "libpspp/message.h"
 #include "libpspp/misc.h"
 #include "libpspp/str.h"
@@ -355,7 +356,7 @@ compare_variables_given_ordering (const void *a_, const void *b_,
       result = a_index < b_index ? -1 : a_index > b_index;
     }
   else
-    result = strcasecmp (var_get_name (a), var_get_name (b));
+    result = utf8_strcasecmp (var_get_name (a), var_get_name (b));
   if (!ordering->forward)
     result = -result;
   return result;
@@ -377,7 +378,7 @@ compare_var_renaming_by_new_name (const void *a_, const void *b_,
   const struct var_renaming *a = a_;
   const struct var_renaming *b = b_;
 
-  return strcasecmp (a->new_name, b->new_name);
+  return utf8_strcasecmp (a->new_name, b->new_name);
 }
 
 /* Returns true if performing VM on dictionary D would not cause
index 2f7c8f6d29ea1601cb23bb7d0b78048dc0eb014a..9e1ca3b0d0bc72f330cbad2532639b3e546ca5cd 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 2010, 2011, 2012 Free Software Foundation, Inc.
 
    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
@@ -417,7 +417,7 @@ parse_group (struct lexer *lexer, struct dictionary *dict,
                   if (width == c->width
                       && value_equal (value, &c->value, width))
                     {
-                      if (!c->warned && strcasecmp (c->label, label))
+                      if (!c->warned && utf8_strcasecmp (c->label, label))
                         {
                           char *s = data_out (value, var_get_encoding (var),
                                               var_get_print_format (var));
index b0d696154b7f212a5c790a87efe7d69b1debd31a..512ec3c2bfeaf99cde80aae28644b08f55225b1a 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2010, 2011, 2012 Free Software Foundation, Inc.
 
    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
@@ -27,6 +27,7 @@
 #include "language/lexer/lexer.h"
 #include "language/lexer/variable-parser.h"
 #include "libpspp/assertion.h"
+#include "libpspp/i18n.h"
 #include "libpspp/message.h"
 #include "libpspp/misc.h"
 #include "libpspp/pool.h"
@@ -68,7 +69,7 @@ cmd_vector (struct lexer *lexer, struct dataset *ds)
            }
 
           for (i = 0; i < vector_cnt; i++)
-            if (!strcasecmp (vectors[i], lex_tokcstr (lexer)))
+            if (!utf8_strcasecmp (vectors[i], lex_tokcstr (lexer)))
              {
                msg (SE, _("Vector name %s is given twice."),
                      lex_tokcstr (lexer));
index 24380e28192f3c22274522db12db8f5d401ea32a..7b845a8d369ebf6da4b736db7c7028cc1ad9814b 100644 (file)
@@ -39,6 +39,7 @@
 #include "libpspp/pool.h"
 #include "libpspp/str.h"
 
+#include "gl/c-strcase.h"
 #include "gl/xalloc.h"
 \f
 /* Declarations. */
@@ -1006,7 +1007,7 @@ compare_names (const char *test, const char *name, bool abbrev_ok)
 static int
 compare_strings (const char *test, const char *name, bool abbrev_ok UNUSED)
 {
-  return strcasecmp (test, name);
+  return c_strcasecmp (test, name);
 }
 
 static bool
index caf294a9d123b4aa28192775ba25aa5d53cb9e7e..de75eeef3bd1f519a5508c55924be76cc6a2bd66 100644 (file)
@@ -27,6 +27,7 @@
 #include "libpspp/cast.h"
 
 #include "gl/c-ctype.h"
+#include "gl/c-strtod.h"
 #include "gl/xmemdup0.h"
 
 enum
@@ -379,7 +380,7 @@ scan_number__ (struct substring s)
   else
     p = xmemdup0 (s.string, s.length);
 
-  number = strtod (p, NULL);
+  number = c_strtod (p, NULL);
 
   if (p != buf)
     free (p);
index 89a5cf0102baf5fa77347ef8051469a8fde8dc77..80c6615c5a5015048ed7e4767ada3e135c26262f 100644 (file)
@@ -25,6 +25,8 @@
 #include "data/identifier.h"
 #include "libpspp/assertion.h"
 #include "libpspp/cast.h"
+#include "libpspp/misc.h"
+
 
 #include "gl/ftoastr.h"
 #include "gl/xalloc.h"
@@ -51,7 +53,7 @@ number_token_to_string (const struct token *token)
 {
   char buffer[DBL_BUFSIZE_BOUND];
 
-  dtoastr (buffer, sizeof buffer, 0, 0, fabs (token->number));
+  c_dtoastr (buffer, sizeof buffer, 0, 0, fabs (token->number));
   return (token->type == T_POS_NUM
           ? xstrdup (buffer)
           : xasprintf ("-%s", buffer));
@@ -163,7 +165,7 @@ token_print (const struct token *token, FILE *stream)
     {
       char s[DBL_BUFSIZE_BOUND];
 
-      dtoastr (s, sizeof s, 0, 0, token->number);
+      c_dtoastr (s, sizeof s, 0, 0, token->number);
       fprintf (stream, "\t%s", s);
     }
   if (token->type == T_ID || token->type == T_STRING || token->string.length)
index 5c19b81e2f5bd8c477f80742464416b8a58f8fb2..8c4f8fe8a61acff13b83aee122c76ed2b4140e6c 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2009, 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
 
    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
@@ -30,6 +30,7 @@
 #include "libpspp/assertion.h"
 #include "libpspp/cast.h"
 #include "libpspp/hash-functions.h"
+#include "libpspp/i18n.h"
 #include "libpspp/hmapx.h"
 #include "libpspp/message.h"
 #include "libpspp/misc.h"
@@ -819,9 +820,9 @@ array_var_set_lookup_var_idx (const struct var_set *vs, const char *name,
   struct hmapx_node *node;
   struct variable **varp;
 
-  HMAPX_FOR_EACH_WITH_HASH (varp, node, hash_case_string (name, 0),
+  HMAPX_FOR_EACH_WITH_HASH (varp, node, utf8_hash_case_string (name, 0),
                             &avs->vars_by_name)
-    if (!strcasecmp (name, var_get_name (*varp)))
+    if (!utf8_strcasecmp (name, var_get_name (*varp)))
       {
         *idx = varp - avs->var;
         return true;
@@ -869,7 +870,7 @@ var_set_create_from_array (struct variable *const *var, size_t var_cnt)
           return NULL;
         }
       hmapx_insert (&avs->vars_by_name, CONST_CAST (void *, &avs->var[i]),
-                    hash_case_string (name, 0));
+                    utf8_hash_case_string (name, 0));
     }
 
   return vs;
index ff6c1fb59e8434a9ae475186a6d48d3e11369587..4d95e62e64f6ea098bad20b7e181927a6a0271a2 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
 
    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
@@ -49,6 +49,7 @@
 #include "math/sort.h"
 #include "math/statistic.h"
 
+#include "gl/c-strcase.h"
 #include "gl/minmax.h"
 #include "gl/xalloc.h"
 
@@ -452,7 +453,7 @@ parse_aggregate_functions (struct lexer *lexer, const struct dictionary *dict,
       exclude = ds_chomp_byte (&function_name, '.') ? MV_SYSTEM : MV_ANY;
 
       for (function = agr_func_tab; function->name; function++)
-       if (!strcasecmp (function->name, ds_cstr (&function_name)))
+       if (!c_strcasecmp (function->name, ds_cstr (&function_name)))
          break;
       if (NULL == function->name)
        {
index 95fb8d173c5661f3a79e06b948457510a8d9e0da..b2c4d4844c5c6cd083b54f0055d5a204c72baa7c 100644 (file)
 #include "libpspp/str.h"
 
 #include "gl/xalloc.h"
-#include "gl/vasnprintf.h"
+#include "gl/c-xvasprintf.h"
 #include "gl/mbiter.h"
 
+
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
 
@@ -295,7 +296,6 @@ cmd_autorecode (struct lexer *lexer, struct dataset *ds)
       for (j = 0; j < n_items; j++)
        {
          const union value *from = &items[j]->from;
-         size_t len;
          char *recoded_value  = NULL;
          char *c;
          const int src_width = items[j]->width;
@@ -315,7 +315,7 @@ cmd_autorecode (struct lexer *lexer, struct dataset *ds)
              recoded_value = recode_string (UTF8, dict_get_encoding (arc->dict), str, src_width);
            }
          else
-           recoded_value = asnprintf (NULL, &len, "%g", from->f);
+           recoded_value = c_xasprintf ("%g", from->f);
          
          /* Remove trailing whitespace */
          for (c = recoded_value; *c != '\0'; c++)
index 54bc49d946a064bd3c4eee8ffdab962d611733c3..8efb5f1fb46209814c77baf4e9d1b499f31ee9bd 100644 (file)
@@ -512,7 +512,7 @@ try_name (const struct dictionary *dict, struct dsc_proc *dsc,
   for (i = 0; i < dsc->var_cnt; i++)
     {
       struct dsc_var *dsc_var = &dsc->vars[i];
-      if (dsc_var->z_name != NULL && !strcasecmp (dsc_var->z_name, name))
+      if (dsc_var->z_name != NULL && !utf8_strcasecmp (dsc_var->z_name, name))
         return false;
     }
   return true;
@@ -1017,7 +1017,7 @@ descriptives_compare_dsc_vars (const void *a_, const void *b_, const void *dsc_)
   int result;
 
   if (dsc->sort_by_stat == DSC_NAME)
-    result = strcasecmp (var_get_name (a->v), var_get_name (b->v));
+    result = utf8_strcasecmp (var_get_name (a->v), var_get_name (b->v));
   else
     {
       double as = a->stats[dsc->sort_by_stat];
index 737c94e3d2a296dfd4f999dac4e822c5c08e5ea3..fbcdeae058e1dd1c47abc8cdcab01c905496598b 100644 (file)
@@ -830,11 +830,11 @@ npar_chisquare (struct lexer *lexer, struct dataset *ds,
     {
       cstp->ranged = true;
       if ( ! lex_force_num (lexer)) return 0;
-      cstp->lo = lex_integer (lexer);
+      cstp->lo = lex_number (lexer);
       lex_get (lexer);
       lex_force_match (lexer, T_COMMA);
       if (! lex_force_num (lexer) ) return 0;
-      cstp->hi = lex_integer (lexer);
+      cstp->hi = lex_number (lexer);
       if ( cstp->lo >= cstp->hi )
        {
          msg (ME,
index 2f81243cf91fbbf726b4bed5d4da506fd5b61804..77e34860ec7b5534c0f2bf6c050af59b614fedd1 100644 (file)
@@ -92,6 +92,8 @@ src_libpspp_liblibpspp_la_SOURCES = \
        src/libpspp/tower.h \
        src/libpspp/u8-istream.c \
        src/libpspp/u8-istream.h \
+       src/libpspp/u8-line.c \
+       src/libpspp/u8-line.h \
        src/libpspp/version.h \
        src/libpspp/zip-private.h \
        src/libpspp/zip-reader.c \
index c43017313a789666ca83bb8373351bc59972a3b0..7a8d8162ecaa5f364c984c1a42827994a7cdfd64 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
 
    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
@@ -102,53 +102,6 @@ hash_string (const char *s, unsigned int basis)
   return hash_bytes (s, strlen (s), basis);
 }
 
-/* Returns a hash value for the N bytes at S, with lowercase and uppercase
-   letters treated as equal, starting from BASIS. */
-unsigned int
-hash_case_bytes (const void *s_, size_t n, unsigned int basis)
-{
-  const char *s = s_;
-  uint32_t a, b, c;
-  uint32_t tmp[3];
-  int i;
-
-  a = b = c = 0xdeadbeef + n + basis;
-
-  while (n >= 12)
-    {
-      for (i = 0; i < 12; i++)
-        ((unsigned char *)tmp)[i] = toupper ((unsigned char) s[i]);
-      a += tmp[0];
-      b += tmp[1];
-      c += tmp[2];
-      HASH_MIX (a, b, c);
-      n -= 12;
-      s += 12;
-    }
-
-  if (n > 0)
-    {
-      memset (tmp, 0, 12);
-      for (i = 0; i < n; i++)
-        ((unsigned char *)tmp)[i] = toupper ((unsigned char) s[i]);
-      a += tmp[0];
-      b += tmp[1];
-      c += tmp[2];
-    }
-
-  HASH_FINAL (a, b, c);
-  return c;
-}
-
-/* Returns a hash value for null-terminated string S, with
-   lowercase and uppercase letters treated as equal, starting
-   from BASIS. */
-unsigned int
-hash_case_string (const char *s, unsigned int basis)
-{
-  return hash_case_bytes (s, strlen (s), basis);
-}
-
 /* Returns a hash value for integer X, starting from BASIS. */
 unsigned int
 hash_int (int x, unsigned int basis)
index 86414fb1c22dc870e6b0a5ac53ba2f1b3a7805d1..f792ba2a96eb50b079f1acec0414d9dbfd19f7ea 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009, 2010, 2012 Free Software Foundation, Inc.
 
    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
@@ -21,8 +21,6 @@
 
 unsigned int hash_bytes (const void *, size_t, unsigned int basis);
 unsigned int hash_string (const char *, unsigned int basis);
-unsigned int hash_case_bytes (const void *, size_t, unsigned int basis);
-unsigned int hash_case_string (const char *, unsigned int basis);
 unsigned int hash_int (int, unsigned int basis);
 unsigned int hash_double (double, unsigned int basis);
 unsigned int hash_pointer (const void *, unsigned int basis);
index 0819299d37abaae54e4f666f046e798818c53a7a..dca85db4f0ee48670a2519bd37bc68f27f58f536 100644 (file)
@@ -27,6 +27,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <unicase.h>
 #include <unigbrk.h>
 
 #include "libpspp/assertion.h"
@@ -39,6 +40,7 @@
 
 #include "gl/c-strcase.h"
 #include "gl/localcharset.h"
+#include "gl/minmax.h"
 #include "gl/xalloc.h"
 #include "gl/relocatable.h"
 #include "gl/xstrndup.h"
@@ -545,12 +547,7 @@ recode_substring_pool (const char *to, const char *from,
 void
 i18n_init (void)
 {
-  setlocale (LC_CTYPE, "");
-  setlocale (LC_COLLATE, "");
-  setlocale (LC_MESSAGES, "");
-#if HAVE_LC_PAPER
-  setlocale (LC_PAPER, "");
-#endif
+  setlocale (LC_ALL, "");
   bindtextdomain (PACKAGE, relocate(locale_dir));
   textdomain (PACKAGE);
 
@@ -598,7 +595,6 @@ set_encoding_from_locale (const char *loc)
       ok = false;
     }
 
-
   setlocale (LC_CTYPE, tmp);
 
   free (tmp);
@@ -660,9 +656,6 @@ get_system_decimal (void)
 {
   char radix_char;
 
-  char *ol = xstrdup (setlocale (LC_NUMERIC, NULL));
-  setlocale (LC_NUMERIC, "");
-
 #if HAVE_NL_LANGINFO
   radix_char = nl_langinfo (RADIXCHAR)[0];
 #else
@@ -673,10 +666,6 @@ get_system_decimal (void)
   }
 #endif
 
-  /* We MUST leave LC_NUMERIC untouched, since it would
-     otherwise interfere with data_{in,out} */
-  setlocale (LC_NUMERIC, ol);
-  free (ol);
   return radix_char;
 }
 
@@ -690,6 +679,109 @@ uc_name (ucs4_t uc, char buffer[16])
   return buffer;
 }
 \f
+/* UTF-8 functions that deal with uppercase/lowercase distinctions. */
+
+/* Returns a hash value for the N bytes of UTF-8 encoded data starting at S,
+   with lowercase and uppercase letters treated as equal, starting from
+   BASIS. */
+unsigned int
+utf8_hash_case_bytes (const char *s, size_t n, unsigned int basis)
+{
+  uint8_t folded_buf[2048];
+  size_t folded_len = sizeof folded_buf;
+  uint8_t *folded_s;
+  unsigned int hash;
+
+  folded_s = u8_casefold (CHAR_CAST (const uint8_t *, s), n,
+                          NULL, UNINORM_NFKD, folded_buf, &folded_len);
+  if (folded_s != NULL)
+    {
+      hash = hash_bytes (folded_s, folded_len, basis);
+      if (folded_s != folded_buf)
+        free (folded_s);
+    }
+  else
+    {
+      if (errno == ENOMEM)
+        xalloc_die ();
+      hash = hash_bytes (s, n, basis);
+    }
+
+  return hash;
+}
+
+/* Returns a hash value for null-terminated UTF-8 string S, with lowercase and
+   uppercase letters treated as equal, starting from BASIS. */
+unsigned int
+utf8_hash_case_string (const char *s, unsigned int basis)
+{
+  return utf8_hash_case_bytes (s, strlen (s), basis);
+}
+
+/* Compares UTF-8 strings A and B case-insensitively.
+   Returns a negative value if A < B, zero if A == B, positive if A > B. */
+int
+utf8_strcasecmp (const char *a, const char *b)
+{
+  return utf8_strncasecmp (a, strlen (a), b, strlen (b));
+}
+
+/* Compares UTF-8 strings A (with length AN) and B (with length BN)
+   case-insensitively.
+   Returns a negative value if A < B, zero if A == B, positive if A > B. */
+int
+utf8_strncasecmp (const char *a, size_t an, const char *b, size_t bn)
+{
+  int result;
+
+  if (u8_casecmp (CHAR_CAST (const uint8_t *, a), an,
+                  CHAR_CAST (const uint8_t *, b), bn,
+                  NULL, UNINORM_NFKD, &result))
+    {
+      if (errno == ENOMEM)
+        xalloc_die ();
+
+      result = memcmp (a, b, MIN (an, bn));
+      if (result == 0)
+        result = an < bn ? -1 : an > bn;
+    }
+
+  return result;
+}
+
+static char *
+utf8_casemap (const char *s,
+              uint8_t *(*f) (const uint8_t *, size_t, const char *, uninorm_t,
+                             uint8_t *, size_t *))
+{
+  char *result;
+  size_t size;
+
+  result = CHAR_CAST (char *,
+                      f (CHAR_CAST (const uint8_t *, s), strlen (s) + 1,
+                         NULL, NULL, NULL, &size));
+  if (result == NULL)
+    {
+      if (errno == ENOMEM)
+        xalloc_die ();
+
+      result = xstrdup (s);
+    }
+  return result;
+}
+
+char *
+utf8_to_upper (const char *s)
+{
+  return utf8_casemap (s, u8_toupper);
+}
+
+char *
+utf8_to_lower (const char *s)
+{
+  return utf8_casemap (s, u8_tolower);
+}
+\f
 bool
 get_encoding_info (struct encoding_info *e, const char *name)
 {
index 4dbf61a29974469b9568779226f8bb30fb01c469..6722b5cec9c3cec743facd4238d50e4f9140c78a 100644 (file)
@@ -67,6 +67,13 @@ void set_default_encoding (const char *enc);
 bool set_encoding_from_locale (const char *loc);
 
 const char *uc_name (ucs4_t uc, char buffer[16]);
+
+unsigned int utf8_hash_case_bytes (const char *, size_t n, unsigned int basis);
+unsigned int utf8_hash_case_string (const char *, unsigned int basis);
+int utf8_strcasecmp (const char *, const char *);
+int utf8_strncasecmp (const char *, size_t, const char *, size_t);
+char *utf8_to_upper (const char *);
+char *utf8_to_lower (const char *);
 \f
 /* Information about character encodings. */
 
index de1ce3f5d51cf92a2632d694a1c53fd56c940daa..c0120df82bfe60d35dbb06c1f64a7095eaf98062 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <config.h>
 #include "misc.h"
+#include <gl/ftoastr.h>
 
 /* Returns the number of digits in X. */
 int
@@ -33,3 +34,23 @@ intlog10 (unsigned x)
   return digits;
 }
 
+
+/* A locale independent version of dtoastr (from gnulib) */
+int
+c_dtoastr (char *buf, size_t bufsize, int flags, int width, double x)
+{
+  int i;
+  int result = dtoastr (buf, bufsize, flags, width, x);
+
+  /* Replace the first , (if any) by a . */
+  for (i = 0; i < result; ++i)
+    {
+      if (buf[i] == ',')
+       {
+         buf[i] = '.';
+         break;
+       }
+    }
+
+  return result;
+}
index c2f865d90c95512d7ec4d3517c68dff96e0181d4..1698297dc4cc51a2694f55d831c740d4eb601694 100644 (file)
@@ -17,6 +17,7 @@
 #if !libpspp_misc_h
 #define libpspp_misc_h 1
 
+#include <stddef.h>
 #include <float.h>
 #include <math.h>
 
@@ -95,5 +96,7 @@ maximize_int (int *dest, int src)
     *dest = src;
 }
 
+int c_dtoastr (char *buf, size_t bufsize, int flags, int width, double x);
+
 
 #endif /* libpspp/misc.h */
index 44e4a1da28ee7183a98eb1e819a755bc55759c0f..d7c71b11f5509f15678693555676c54cc4662e3f 100644 (file)
@@ -28,6 +28,8 @@
 #include "libpspp/message.h"
 #include "libpspp/pool.h"
 
+#include "gl/c-ctype.h"
+#include "gl/c-vasnprintf.h"
 #include "gl/relocatable.h"
 #include "gl/minmax.h"
 #include "gl/xalloc.h"
@@ -232,20 +234,26 @@ str_copy_buf_trunc (char *dst, size_t dst_size,
   dst[dst_len] = '\0';
 }
 
-/* Converts each byte in S to uppercase. */
+/* Converts each byte in S to uppercase.
+
+   This is suitable only for ASCII strings.  Use utf8_to_upper() for UTF-8
+   strings.*/
 void
 str_uppercase (char *s)
 {
   for (; *s != '\0'; s++)
-    *s = toupper ((unsigned char) *s);
+    *s = c_toupper ((unsigned char) *s);
 }
 
-/* Converts each byte in S to lowercase. */
+/* Converts each byte in S to lowercase.
+
+   This is suitable only for ASCII strings.  Use utf8_to_lower() for UTF-8
+   strings.*/
 void
 str_lowercase (char *s)
 {
   for (; *s != '\0'; s++)
-    *s = tolower ((unsigned char) *s);
+    *s = c_tolower ((unsigned char) *s);
 }
 
 /* Converts NUMBER into a string in 26-adic notation in BUFFER,
@@ -1499,22 +1507,36 @@ ds_put_format (struct string *st, const char *format, ...)
   va_end (args);
 }
 
-/* Formats FORMAT as a printf string and appends the result to ST. */
+/* Formats FORMAT as a printf string as if in the C locale and appends the result to ST. */
 void
-ds_put_vformat (struct string *st, const char *format, va_list args_)
+ds_put_c_format (struct string *st, const char *format, ...)
+{
+  va_list args;
+
+  va_start (args, format);
+  ds_put_c_vformat (st, format, args);
+  va_end (args);
+}
+
+
+/* Formats FORMAT as a printf string, using fmt_func (a snprintf like function) 
+   and appends the result to ST. */
+static void
+ds_put_vformat_int (struct string *st, const char *format, va_list args_,
+                   int (*fmt_func) (char *, size_t, const char *, va_list))
 {
   int avail, needed;
   va_list args;
 
   va_copy (args, args_);
   avail = st->ss.string != NULL ? st->capacity - st->ss.length + 1 : 0;
-  needed = vsnprintf (st->ss.string + st->ss.length, avail, format, args);
+  needed = fmt_func (st->ss.string + st->ss.length, avail, format, args);
   va_end (args);
 
   if (needed >= avail)
     {
       va_copy (args, args_);
-      vsprintf (ds_put_uninit (st, needed), format, args);
+      fmt_func (ds_put_uninit (st, needed), needed + 1, format, args);
       va_end (args);
     }
   else
@@ -1527,13 +1549,36 @@ ds_put_vformat (struct string *st, const char *format, va_list args_)
           avail = st->capacity - st->ss.length + 1;
 
           va_copy (args, args_);
-          needed = vsnprintf (ds_end (st), avail, format, args);
+          needed = fmt_func (ds_end (st), avail, format, args);
           va_end (args);
         }
       st->ss.length += needed;
     }
 }
 
+
+static int
+vasnwrapper (char *str, size_t size,  const char *format, va_list ap)
+{
+  c_vasnprintf (str, &size, format, ap);
+  return size;
+}
+
+/* Formats FORMAT as a printf string and appends the result to ST. */
+void
+ds_put_vformat (struct string *st, const char *format, va_list args_)
+{
+  ds_put_vformat_int (st, format, args_, vsnprintf);
+}
+
+/* Formats FORMAT as a printf string, as if in the C locale, 
+   and appends the result to ST. */
+void
+ds_put_c_vformat (struct string *st, const char *format, va_list args_)
+{
+  ds_put_vformat_int (st, format, args_, vasnwrapper);
+}
+
 /* Appends byte CH to ST. */
 void
 ds_put_byte (struct string *st, int ch)
index e017696cf60bdc1efc9dd12a744e3d9b7befeec5..306bd8ba89f110762dbf31f04abace081227b0cc 100644 (file)
@@ -228,8 +228,14 @@ void ds_put_cstr (struct string *, const char *);
 void ds_put_substring (struct string *, struct substring);
 void ds_put_vformat (struct string *st, const char *, va_list)
      PRINTF_FORMAT (2, 0);
+void ds_put_c_vformat (struct string *st, const char *, va_list)
+     PRINTF_FORMAT (2, 0);
+
 void ds_put_format (struct string *, const char *, ...)
      PRINTF_FORMAT (2, 3);
+void ds_put_c_format (struct string *, const char *, ...)
+     PRINTF_FORMAT (2, 3);
+
 char *ds_put_uninit (struct string *st, size_t incr);
 
 char *ds_splice_uninit (struct string *, size_t ofs, size_t old_len,
index d3e5144de7276da98af7f0af99a88c12f8377b1e..7b8d398d03c8d6aab505d814de81800f2a2840d1 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 2009, 2010, 2012 Free Software Foundation, Inc.
 
    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
@@ -22,6 +22,7 @@
 #include <string.h>
 
 #include "libpspp/hash-functions.h"
+#include "libpspp/i18n.h"
 #include "libpspp/string-set.h"
 #include "libpspp/stringi-set.h"
 
@@ -148,7 +149,7 @@ stringi_map_find (const struct stringi_map *map, const char *key)
 struct stringi_map_node *
 stringi_map_find_node (const struct stringi_map *map, const char *key)
 {
-  return stringi_map_find_node__ (map, key, hash_case_string (key, 0));
+  return stringi_map_find_node__ (map, key, utf8_hash_case_string (key, 0));
 }
 
 /* If MAP contains KEY (or an equivalent with different case) as a key, deletes
@@ -175,7 +176,7 @@ struct stringi_map_node *
 stringi_map_insert (struct stringi_map *map, const char *key,
                     const char *value)
 {
-  unsigned int hash = hash_case_string (key, 0);
+  unsigned int hash = utf8_hash_case_string (key, 0);
   struct stringi_map_node *node = stringi_map_find_node__ (map, key, hash);
   if (node == NULL)
     node = stringi_map_insert__ (map, xstrdup (key), xstrdup (value), hash);
@@ -189,7 +190,7 @@ stringi_map_insert (struct stringi_map *map, const char *key,
 struct stringi_map_node *
 stringi_map_insert_nocopy (struct stringi_map *map, char *key, char *value)
 {
-  unsigned int hash = hash_case_string (key, 0);
+  unsigned int hash = utf8_hash_case_string (key, 0);
   struct stringi_map_node *node = stringi_map_find_node__ (map, key, hash);
   if (node == NULL)
     node = stringi_map_insert__ (map, key, value, hash);
@@ -208,7 +209,7 @@ struct stringi_map_node *
 stringi_map_replace (struct stringi_map *map, const char *key,
                      const char *value)
 {
-  unsigned int hash = hash_case_string (key, 0);
+  unsigned int hash = utf8_hash_case_string (key, 0);
   struct stringi_map_node *node = stringi_map_find_node__ (map, key, hash);
   if (node == NULL)
     node = stringi_map_insert__ (map, xstrdup (key), xstrdup (value), hash);
@@ -224,7 +225,7 @@ stringi_map_replace (struct stringi_map *map, const char *key,
 struct stringi_map_node *
 stringi_map_replace_nocopy (struct stringi_map *map, char *key, char *value)
 {
-  unsigned int hash = hash_case_string (key, 0);
+  unsigned int hash = utf8_hash_case_string (key, 0);
   struct stringi_map_node *node = stringi_map_find_node__ (map, key, hash);
   if (node == NULL)
     node = stringi_map_insert__ (map, key, value, hash);
@@ -242,7 +243,7 @@ stringi_map_replace_nocopy (struct stringi_map *map, char *key, char *value)
 bool
 stringi_map_delete (struct stringi_map *map, const char *key)
 {
-  return stringi_map_delete__ (map, key, hash_case_string (key, 0));
+  return stringi_map_delete__ (map, key, utf8_hash_case_string (key, 0));
 }
 
 /* Deletes NODE from MAP and destroys the node and its key and value. */
@@ -344,7 +345,7 @@ stringi_map_find_node__ (const struct stringi_map *map, const char *key,
 
   HMAP_FOR_EACH_WITH_HASH (node, struct stringi_map_node, hmap_node,
                            hash, &map->hmap)
-    if (!strcasecmp (key, node->key))
+    if (!utf8_strcasecmp (key, node->key))
       return node;
 
   return NULL;
index a7ae6994711606eb0c5a2e5acec67ecb124ea71c..b442a41567805a9ca92d431fc413468f5f9abc22 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 2009, 2010, 2012 Free Software Foundation, Inc.
 
    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
@@ -26,6 +26,7 @@
 
 #include "libpspp/cast.h"
 #include "libpspp/hash-functions.h"
+#include "libpspp/i18n.h"
 
 #include "gl/xalloc.h"
 
@@ -88,7 +89,7 @@ stringi_set_contains (const struct stringi_set *set, const char *s)
 struct stringi_set_node *
 stringi_set_find_node (const struct stringi_set *set, const char *s)
 {
-  return stringi_set_find_node__ (set, s, hash_case_string (s, 0));
+  return stringi_set_find_node__ (set, s, utf8_hash_case_string (s, 0));
 }
 
 /* Inserts a copy of S into SET.  Returns true if successful, false if SET
@@ -96,7 +97,7 @@ stringi_set_find_node (const struct stringi_set *set, const char *s)
 bool
 stringi_set_insert (struct stringi_set *set, const char *s)
 {
-  unsigned int hash = hash_case_string (s, 0);
+  unsigned int hash = utf8_hash_case_string (s, 0);
   if (!stringi_set_find_node__ (set, s, hash))
     {
       stringi_set_insert__ (set, xstrdup (s), hash);
@@ -112,7 +113,7 @@ stringi_set_insert (struct stringi_set *set, const char *s)
 bool
 stringi_set_insert_nocopy (struct stringi_set *set, char *s)
 {
-  unsigned int hash = hash_case_string (s, 0);
+  unsigned int hash = utf8_hash_case_string (s, 0);
   if (!stringi_set_find_node__ (set, s, hash))
     {
       stringi_set_insert__ (set, s, hash);
@@ -130,7 +131,7 @@ stringi_set_insert_nocopy (struct stringi_set *set, char *s)
 bool
 stringi_set_delete (struct stringi_set *set, const char *s)
 {
-  return stringi_set_delete__ (set, s, hash_case_string (s, 0));
+  return stringi_set_delete__ (set, s, utf8_hash_case_string (s, 0));
 }
 
 /* Deletes NODE from SET, and frees NODE and its string. */
@@ -258,7 +259,7 @@ compare_strings (const void *a_, const void *b_)
 {
   const char *const *a = a_;
   const char *const *b = b_;
-  return strcasecmp (*a, *b);
+  return utf8_strcasecmp (*a, *b);
 }
 
 /* Allocates and returns an array that points to each of the strings in SET.
@@ -267,7 +268,7 @@ compare_strings (const void *a_, const void *b_)
    caller it is responsible for freeing the returned array itself (with
    free()).
 
-   The returned array is ordered according to strcasecmp(). */
+   The returned array is ordered according to utf8_strcasecmp(). */
 char **
 stringi_set_get_sorted_array (const struct stringi_set *set)
 {
@@ -286,7 +287,7 @@ stringi_set_find_node__ (const struct stringi_set *set, const char *s,
 
   HMAP_FOR_EACH_WITH_HASH (node, struct stringi_set_node, hmap_node,
                            hash, &set->hmap)
-    if (!strcasecmp (s, node->string))
+    if (!utf8_strcasecmp (s, node->string))
       return node;
 
   return NULL;
diff --git a/src/libpspp/u8-line.c b/src/libpspp/u8-line.c
new file mode 100644 (file)
index 0000000..34e6509
--- /dev/null
@@ -0,0 +1,221 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2011, 2012 Free Software Foundation, Inc.
+
+   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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include "libpspp/u8-line.h"
+#include <unistr.h>
+#include <uniwidth.h>
+#include "libpspp/cast.h"
+#include "libpspp/str.h"
+
+/* Initializes LINE as an empty u8_line. */
+void
+u8_line_init (struct u8_line *line)
+{
+  ds_init_empty (&line->s);
+  line->width = 0;
+}
+
+/* Frees data owned by LINE. */
+void
+u8_line_destroy (struct u8_line *line)
+{
+  ds_destroy (&line->s);
+}
+
+/* Clears LINE to zero length. */
+void
+u8_line_clear (struct u8_line *line)
+{
+  ds_clear (&line->s);
+  line->width = 0;
+}
+
+static int
+u8_mb_to_display (int *wp, const uint8_t *s, size_t n)
+{
+  size_t ofs;
+  ucs4_t uc;
+  int w;
+
+  ofs = u8_mbtouc (&uc, s, n);
+  if (ofs < n && s[ofs] == '\b')
+    {
+      ofs++;
+      ofs += u8_mbtouc (&uc, s + ofs, n - ofs);
+    }
+
+  w = uc_width (uc, "UTF-8");
+  if (w <= 0)
+    {
+      *wp = 0;
+      return ofs;
+    }
+
+  while (ofs < n)
+    {
+      int mblen = u8_mbtouc (&uc, s + ofs, n - ofs);
+      if (uc_width (uc, "UTF-8") > 0)
+        break;
+      ofs += mblen;
+    }
+
+  *wp = w;
+  return ofs;
+}
+
+/* Position of a character within a u8_line. */
+struct u8_pos
+  {
+    /* 0-based display columns.
+
+       For a single-width character, x1 == x0 + 1.
+       For a double-width character, x1 == x0 + 2. */
+    int x0;
+    int x1;
+
+    /* Byte offsets.
+
+       For an ordinary ASCII character, ofs1 == ofs0 + 1.
+       For Unicode code point 0x80 or higher, 2 <= ofs1 - ofs0 <= 4. */
+    size_t ofs0;
+    size_t ofs1;
+  };
+
+static void
+u8_line_find_pos (struct u8_line *line, int target_x, struct u8_pos *c)
+{
+  const uint8_t *s = CHAR_CAST (const uint8_t *, ds_cstr (&line->s));
+  size_t length = ds_length (&line->s);
+  size_t ofs;
+  int mblen;
+  int x;
+
+  x = 0;
+  for (ofs = 0; ; ofs += mblen)
+    {
+      int w;
+
+      mblen = u8_mb_to_display (&w, s + ofs, length - ofs);
+      if (x + w > target_x)
+        {
+          c->x0 = x;
+          c->x1 = x + w;
+          c->ofs0 = ofs;
+          c->ofs1 = ofs + mblen;
+          return;
+        }
+      x += w;
+    }
+}
+
+/* Prepares LINE to write N bytes of characters that comprise X1-X0 column
+   widths starting at 0-based column X0.  Returns the first byte of the N for
+   the caller to fill in. */
+char *
+u8_line_reserve (struct u8_line *line, int x0, int x1, int n)
+{
+  if (x0 >= line->width)
+    {
+      /* The common case: adding new characters at the end of a line. */
+      ds_put_byte_multiple (&line->s, ' ', x0 - line->width);
+      line->width = x1;
+      return ds_put_uninit (&line->s, n);
+    }
+  else if (x0 == x1)
+    return NULL;
+  else
+    {
+      /* An unusual case: overwriting characters in the middle of a line.  We
+         don't keep any kind of mapping from bytes to display positions, so we
+         have to iterate over the whole line starting from the beginning. */
+      struct u8_pos p0, p1;
+      char *s;
+
+      /* Find the positions of the first and last character.  We must find both
+         characters' positions before changing the line, because that would
+         prevent finding the other character's position. */
+      u8_line_find_pos (line, x0, &p0);
+      if (x1 < line->width)
+        u8_line_find_pos (line, x1, &p1);
+
+      /* If a double-width character occupies both x0 - 1 and x0, then replace
+         its first character width by '?'. */
+      s = ds_data (&line->s);
+      while (p0.x0 < x0)
+        {
+          s[p0.ofs0++] = '?';
+          p0.x0++;
+        }
+
+      if (x1 >= line->width)
+        {
+          ds_truncate (&line->s, p0.ofs0);
+          line->width = x1;
+          return ds_put_uninit (&line->s, n);
+        }
+
+      /* If a double-width character occupies both x1 - 1 and x1, then replace
+         its second character width by '?'. */
+      if (p1.x0 < x1)
+        {
+          do
+            {
+              s[--p1.ofs1] = '?';
+              p1.x0++;
+            }
+          while (p1.x0 < x1);
+          return ds_splice_uninit (&line->s, p0.ofs0, p1.ofs1 - p0.ofs0, n);
+        }
+
+      return ds_splice_uninit (&line->s, p0.ofs0, p1.ofs0 - p0.ofs0, n);
+    }
+}
+
+/* Writes the N bytes of characters that comprise X1-X0 column widths into LINE
+   starting at 0-based column X0. */
+void
+u8_line_put (struct u8_line *line, int x0, int x1, const char *s, int n)
+{
+  memcpy (u8_line_reserve (line, x0, x1, n), s, n);
+}
+
+/* Changes the width of LINE to X column widths.  If X is longer than LINE's
+   previous width, LINE is extended by appending spaces.  If X is shorter than
+   LINE's previous width, LINE is shortened by removing trailing characters. */
+void
+u8_line_set_length (struct u8_line *line, int x)
+{
+  if (x > line->width)
+    {
+      ds_put_byte_multiple (&line->s, ' ', x - line->width);
+      line->width = x;
+    }
+  else if (x < line->width)
+    {
+      struct u8_pos pos;
+
+      u8_line_find_pos (line, x, &pos);
+      ds_truncate (&line->s, pos.ofs0);
+      line->width = pos.x0;
+      if (x > line->width)
+        {
+          ds_put_byte_multiple (&line->s, '?', x - line->width);
+          line->width = x;
+        }
+    }
+}
diff --git a/src/libpspp/u8-line.h b/src/libpspp/u8-line.h
new file mode 100644 (file)
index 0000000..2674e2e
--- /dev/null
@@ -0,0 +1,42 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2011, 2012 Free Software Foundation, Inc.
+
+   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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#ifndef LIBPSPP_U8_LINE_H
+#define LIBPSPP_U8_LINE_H 1
+
+#include "libpspp/str.h"
+
+struct pool;
+
+/* A line of text, encoded in UTF-8, with support functions that properly
+   handle double-width characters and backspaces.
+
+   Designed to make appending text fast, and access and modification of other
+   column positions possible. */
+struct u8_line
+  {
+    struct string s;            /* Content, in UTF-8. */
+    size_t width;               /* Display width, in character positions. */
+  };
+
+void u8_line_init (struct u8_line *);
+void u8_line_destroy (struct u8_line *);
+void u8_line_clear (struct u8_line *);
+char *u8_line_reserve (struct u8_line *, int x0, int x1, int n);
+void u8_line_put (struct u8_line *, int x0, int x1, const char *s, int n);
+void u8_line_set_length (struct u8_line *, int x);
+
+#endif /* libpspp/u8-line.h */
index 9614e6aef899d88353142f7f1827a7a9ad5462d1..756235c5cbf6cae7183e19d4a287ff6566840c7a 100644 (file)
@@ -22,8 +22,8 @@
 #include <stdint.h>
 #include <stdlib.h>
 #include <signal.h>
-#include <unistd.h>
 #include <unilbrk.h>
+#include <unistd.h>
 #include <unistr.h>
 #include <uniwidth.h>
 
@@ -35,6 +35,7 @@
 #include "libpspp/message.h"
 #include "libpspp/start-date.h"
 #include "libpspp/string-map.h"
+#include "libpspp/u8-line.h"
 #include "libpspp/version.h"
 #include "output/ascii.h"
 #include "output/cairo.h"
@@ -128,13 +129,6 @@ make_box_index (int left, int right, int top, int bottom)
   return ((right * 3 + bottom) * 3 + left) * 3 + top;
 }
 
-/* A line of text. */
-struct ascii_line
-  {
-    struct string s;            /* Content, in UTF-8. */
-    size_t width;               /* Display width, in character positions. */
-  };
-
 /* How to emphasize text. */
 enum emphasis_style
   {
@@ -174,7 +168,7 @@ struct ascii_driver
     FILE *file;                 /* Output file. */
     bool error;                 /* Output error? */
     int page_number;           /* Current page number. */
-    struct ascii_line *lines;   /* Page content. */
+    struct u8_line *lines;      /* Page content. */
     int allocated_lines;        /* Number of lines allocated. */
     int chart_cnt;              /* Number of charts so far. */
     int y;
@@ -210,11 +204,7 @@ reallocate_lines (struct ascii_driver *a)
       int i;
       a->lines = xnrealloc (a->lines, a->length, sizeof *a->lines);
       for (i = a->allocated_lines; i < a->length; i++)
-        {
-          struct ascii_line *line = &a->lines[i];
-          ds_init_empty (&line->s);
-          line->width = 0;
-        }
+        u8_line_init (&a->lines[i]);
       a->allocated_lines = a->length;
     }
 }
@@ -380,7 +370,7 @@ ascii_destroy (struct output_driver *driver)
   free (a->file_name);
   free (a->chart_file_name);
   for (i = 0; i < a->allocated_lines; i++)
-    ds_destroy (&a->lines[i].s);
+    u8_line_destroy (&a->lines[i]);
   free (a->lines);
   free (a);
 }
@@ -698,136 +688,11 @@ ascii_draw_cell (void *a_, const struct table_cell *cell,
   ascii_layout_cell (a, cell, bb, clip, &w, &h);
 }
 
-static int
-u8_mb_to_display (int *wp, const uint8_t *s, size_t n)
-{
-  size_t ofs;
-  ucs4_t uc;
-  int w;
-
-  ofs = u8_mbtouc (&uc, s, n);
-  if (ofs < n && s[ofs] == '\b')
-    {
-      ofs++;
-      ofs += u8_mbtouc (&uc, s + ofs, n - ofs);
-    }
-
-  w = uc_width (uc, "UTF-8");
-  if (w <= 0)
-    {
-      *wp = 0;
-      return ofs;
-    }
-
-  while (ofs < n)
-    {
-      int mblen = u8_mbtouc (&uc, s + ofs, n - ofs);
-      if (uc_width (uc, "UTF-8") > 0)
-        break;
-      ofs += mblen;
-    }
-
-  *wp = w;
-  return ofs;
-}
-
-struct ascii_pos
-  {
-    int x0;
-    int x1;
-    size_t ofs0;
-    size_t ofs1;
-  };
-
-static void
-find_ascii_pos (struct ascii_line *line, int target_x, struct ascii_pos *c)
-{
-  const uint8_t *s = CHAR_CAST (const uint8_t *, ds_cstr (&line->s));
-  size_t length = ds_length (&line->s);
-  size_t ofs;
-  int mblen;
-  int x;
-
-  x = 0;
-  for (ofs = 0; ; ofs += mblen)
-    {
-      int w;
-
-      mblen = u8_mb_to_display (&w, s + ofs, length - ofs);
-      if (x + w > target_x)
-        {
-          c->x0 = x;
-          c->x1 = x + w;
-          c->ofs0 = ofs;
-          c->ofs1 = ofs + mblen;
-          return;
-        }
-      x += w;
-    }
-}
-
 static char *
 ascii_reserve (struct ascii_driver *a, int y, int x0, int x1, int n)
 {
-  struct ascii_line *line;
   assert (y < a->allocated_lines);
-  line = &a->lines[y];
-
-  if (x0 >= line->width)
-    {
-      /* The common case: adding new characters at the end of a line. */
-      ds_put_byte_multiple (&line->s, ' ', x0 - line->width);
-      line->width = x1;
-      return ds_put_uninit (&line->s, n);
-    }
-  else if (x0 == x1)
-    return NULL;
-  else
-    {
-      /* An unusual case: overwriting characters in the middle of a line.  We
-         don't keep any kind of mapping from bytes to display positions, so we
-         have to iterate over the whole line starting from the beginning. */
-      struct ascii_pos p0, p1;
-      char *s;
-
-      /* Find the positions of the first and last character.  We must find the
-         both characters' positions before changing the line, because that
-         would prevent finding the other character's position. */
-      find_ascii_pos (line, x0, &p0);
-      if (x1 < line->width)
-        find_ascii_pos (line, x1, &p1);
-
-      /* If a double-width character occupies both x0 - 1 and x0, then replace
-         its first character width by '?'. */
-      s = ds_data (&line->s);
-      while (p0.x0 < x0)
-        {
-          s[p0.ofs0++] = '?';
-          p0.x0++;
-        }
-
-      if (x1 >= line->width)
-        {
-          ds_truncate (&line->s, p0.ofs0);
-          line->width = x1;
-          return ds_put_uninit (&line->s, n);
-        }
-
-      /* If a double-width character occupies both x1 - 1 and x1, then we need
-         to replace its second character width by '?'. */
-      if (p1.x0 < x1)
-        {
-          do
-            {
-              s[--p1.ofs1] = '?';
-              p1.x0++;
-            }
-          while (p1.x0 < x1);
-          return ds_splice_uninit (&line->s, p0.ofs0, p1.ofs1 - p0.ofs0, n);
-        }
-
-      return ds_splice_uninit (&line->s, p0.ofs0, p1.ofs0 - p0.ofs0, n);
-    }
+  return u8_line_reserve (&a->lines[y], x0, x1, n);
 }
 
 static void
@@ -1078,6 +943,19 @@ ascii_test_write (struct output_driver *driver,
 
   a->y = 1;
 }
+
+void
+ascii_test_set_length (struct output_driver *driver, int y, int length)
+{
+  struct ascii_driver *a = ascii_driver_cast (driver);
+
+  if (a->file == NULL && !ascii_open_page (a))
+    return;
+
+  if (y < 0 || y >= a->length)
+    return;
+  u8_line_set_length (&a->lines[y], length);
+}
 \f
 /* ascii_close_page () and support routines. */
 
@@ -1132,11 +1010,7 @@ ascii_open_page (struct ascii_driver *a)
   reallocate_lines (a);
 
   for (i = 0; i < a->length; i++)
-    {
-      struct ascii_line *line = &a->lines[i];
-      ds_clear (&line->s);
-      line->width = 0;
-    }
+    u8_line_clear (&a->lines[i]);
 
   return true;
 }
@@ -1195,7 +1069,7 @@ ascii_close_page (struct ascii_driver *a)
   any_blank = false;
   for (y = 0; y < a->allocated_lines; y++)
     {
-      struct ascii_line *line = &a->lines[y];
+      struct u8_line *line = &a->lines[y];
 
       if (a->squeeze_blank_lines && y > 0 && line->width == 0)
         any_blank = true;
index 5d324d84646a130e1339939a4f95415ab40428c4..bf68cafec7a82bd4842ff2b151b9d71bb45a68de 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2011 Free Software Foundation, Inc.
+   Copyright (C) 2011, 2012 Free Software Foundation, Inc.
 
    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
@@ -21,5 +21,6 @@ struct output_driver;
 
 void ascii_test_write (struct output_driver *,
                        const char *s, int x, int y, unsigned int options);
+void ascii_test_set_length (struct output_driver *, int y, int length);
 
 #endif /* ascii.h */
index 60894f1cc25f81b3b21ae862ee646e0ac1d3c0ac..faeef969b09b247e4b4d3a7f468e35a2039bccbe 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
 
    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
@@ -29,6 +29,7 @@
 #include "data/file-name.h"
 #include "libpspp/str.h"
 
+#include "gl/c-strcase.h"
 #include "gl/error.h"
 
 #include "gettext.h"
@@ -135,7 +136,7 @@ parse_unit (const char *unit)
 
   unit += strspn (unit, CC_SPACES);
   for (p = units; p < units + sizeof units / sizeof *units; p++)
-    if (!strcasecmp (unit, p->name))
+    if (!c_strcasecmp (unit, p->name))
       return p->factor;
   return 0.0;
 }
index 8b54ed584afe84798b8475e46295bed1b66060fa..5d477d30c554c172b9e8a1493810962bbd164316 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "dialog-common.h"
 
+#include <gl/c-xvasprintf.h>
 #include <language/stats/aggregate.h>
 
 #include <ui/syntax-gen.h>
@@ -426,15 +427,14 @@ on_acr_change (const struct aggregate *agg, GtkTreeView *tv)
   gtk_entry_set_text (GTK_ENTRY (agg->summary_var_label_entry), label);
   gtk_entry_set_text (GTK_ENTRY (agg->summary_sv_entry), srcvar);
   
-  text = g_strdup_printf ("%g", arg1);
+  text = c_xasprintf ("%g", arg1);
   gtk_entry_set_text (GTK_ENTRY (agg->summary_arg1_entry), text);
   g_free (text);
 
-  text = g_strdup_printf ("%g", arg2);
+  text = c_xasprintf ("%g", arg2);
   gtk_entry_set_text (GTK_ENTRY (agg->summary_arg2_entry), text);
   g_free (text);
 
-
   gtk_combo_box_set_active (GTK_COMBO_BOX (agg->function_combo), f_idx);
 }
 
@@ -670,17 +670,22 @@ append_summary_spec (const struct aggregate *agg, GtkTreeIter *iter, GString *st
 
   if ( has_src_vars != AGR_SV_NO)
     {
-      g_string_append (string, " (");
+      struct string dss;
+      ds_init_cstr (&dss, " (");
       
-      g_string_append (string, srcvar);
+      ds_put_cstr (&dss, srcvar);
 
       if ( arity > 0)
-       g_string_append_printf (string, ", %g", arg1);
+       ds_put_c_format (&dss, ", %g", arg1);
 
       if ( arity > 1)
-       g_string_append_printf (string, ", %g", arg2);
+       ds_put_c_format (&dss, ", %g", arg2);
+
+      ds_put_cstr (&dss, ")");
+
+      g_string_append (string, ds_cstr (&dss));
 
-      g_string_append (string, ")");
+      ds_destroy (&dss);
     }
 
    free (label);
index 5d4cc4b431038ad00ab77e550214b605c308e9d1..df408921d1e72d08d188fef40b222baee2703098 100644 (file)
@@ -82,40 +82,35 @@ static char *
 generate_syntax (const struct chisquare_dialog *scd)
 {
   gchar *text;
-  GString *string;
+  struct string dss;
 
+  ds_init_cstr (&dss, "NPAR TEST\n\t/CHISQUARE=");
 
-  string = g_string_new ("NPAR TEST\n\t/CHISQUARE=");
-
-  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (scd->var_view), 0, string);
-
+  psppire_var_view_append_names_str (PSPPIRE_VAR_VIEW (scd->var_view), 0, &dss);
 
   if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (scd->range_button)))
     {
-      g_string_append (string, "(");
+      ds_put_cstr (&dss, "(");
       
-      g_string_append (string
+      ds_put_cstr (&dss
                       gtk_entry_get_text (GTK_ENTRY (scd->value_lower)));
 
-      g_string_append (string, ", ");
+      ds_put_cstr (&dss, ", ");
 
-      g_string_append (string,
+      ds_put_cstr (&dss,
                       gtk_entry_get_text (GTK_ENTRY (scd->value_upper)));
 
-      g_string_append (string, ")");
+      ds_put_cstr (&dss, ")");
     }
 
-
-
-
   if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (scd->values_button)))
     {
       GtkListStore *ls = scd->expected_list;
       GtkTreeIter iter;
       gboolean ok;
 
-      g_string_append (string, "\n\t");
-      g_string_append (string, "/EXPECTED = ");
+      ds_put_cstr (&dss, "\n\t");
+      ds_put_cstr (&dss, "/EXPECTED = ");
 
       
       for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(ls),
@@ -127,18 +122,15 @@ generate_syntax (const struct chisquare_dialog *scd)
 
          gtk_tree_model_get (GTK_TREE_MODEL (ls), &iter, 0, &v, -1);
 
-         g_string_append_printf (string, " %g", v);
+         ds_put_c_format (&dss, " %g", v);
        }
-
-
-
     }
 
-  g_string_append (string, ".\n");
+  ds_put_cstr (&dss, ".\n");
 
-  text = string->str;
+  text = ds_steal_cstr (&dss);
 
-  g_string_free (string, FALSE);
+  ds_destroy (&dss);
 
   return text;
 }
index 135cc473304fa91c43eaf9b59ea5eec75bfecafb..c73032292bf4473da53bc945316d69834c1525a9 100644 (file)
@@ -234,17 +234,19 @@ generate_syntax (const struct cnt_dialog *cnt)
   const gchar *s = NULL;
   gboolean ok;
   GtkTreeIter iter;
-  GString *str = g_string_sized_new (100);
+  struct string dds;
+  
+  ds_init_empty (&dds);
 
-  g_string_append (str, "\nCOUNT ");
+  ds_put_cstr (&dds, "\nCOUNT ");
 
-  g_string_append (str, gtk_entry_get_text (GTK_ENTRY (cnt->target)));
+  ds_put_cstr (&dds, gtk_entry_get_text (GTK_ENTRY (cnt->target)));
 
-  g_string_append (str, " =");
+  ds_put_cstr (&dds, " =");
 
-  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (cnt->variable_treeview), 0, str);
+  psppire_var_view_append_names_str (PSPPIRE_VAR_VIEW (cnt->variable_treeview), 0, &dds);
 
-  g_string_append (str, "(");
+  ds_put_cstr (&dds, "(");
   for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (cnt->value_list),
                                           &iter);
        ok;
@@ -258,36 +260,31 @@ generate_syntax (const struct cnt_dialog *cnt)
 
       ov = g_value_get_boxed (&a_value);
 
-      g_string_append (str, " ");
-      old_value_append_syntax (str, ov);
+      ds_put_cstr (&dds, " ");
+      old_value_append_syntax (&dds, ov);
     }
-  g_string_append (str, ").");
+  ds_put_cstr (&dds, ").");
 
 
   s = gtk_entry_get_text (GTK_ENTRY (cnt->label));
   if (0 != strcmp (s, ""))
   {
-    struct string ds;
-    ds_init_empty (&ds);
-    g_string_append (str, "\nVARIABLE LABELS ");
+    ds_put_cstr (&dds, "\nVARIABLE LABELS ");
 
-    g_string_append (str, gtk_entry_get_text (GTK_ENTRY (cnt->target)));
+    ds_put_cstr (&dds, gtk_entry_get_text (GTK_ENTRY (cnt->target)));
 
-    g_string_append (str, " ");
+    ds_put_cstr (&dds, " ");
 
-    syntax_gen_string (&ds, ss_cstr (s));
+    syntax_gen_string (&dds, ss_cstr (s));
 
-    g_string_append (str, ds_cstr (&ds));
-
-    g_string_append (str, ".");
-    ds_destroy (&ds);
+    ds_put_cstr (&dds, ".");
   }
 
-  g_string_append (str, "\nEXECUTE.\n");
+  ds_put_cstr (&dds, "\nEXECUTE.\n");
 
-  text = str->str;
+  text = ds_steal_cstr (&dds);
 
-  g_string_free (str, FALSE);
+  ds_destroy (&dds);
 
   return text;
 }
index 7992b94c71c91c7637c2252fc51b1620c42e74de..d656366cf06bade3e0a578ca33e2dadb0a38c472 100644 (file)
@@ -226,22 +226,23 @@ static gchar * generate_syntax (const struct oneway_anova_dialog *ow)
   gint i;
   gboolean descriptives = gtk_toggle_button_get_active (ow->descriptives);
   gboolean homogeneity = gtk_toggle_button_get_active (ow->homogeneity);
+  struct string dss;
 
-  GString *str = g_string_new ("ONEWAY /VARIABLES=");
+  ds_init_cstr (&dss, "ONEWAY /VARIABLES=");
 
-  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (ow->vars_treeview), 0, str);
+  psppire_var_view_append_names_str (PSPPIRE_VAR_VIEW (ow->vars_treeview), 0, &dss);
 
-  g_string_append (str, " BY ");
+  ds_put_cstr (&dss, " BY ");
 
-  g_string_append (str, gtk_entry_get_text (GTK_ENTRY (ow->factor_entry)));
+  ds_put_cstr (&dss, gtk_entry_get_text (GTK_ENTRY (ow->factor_entry)));
 
   if (descriptives || homogeneity )
     {
-      g_string_append (str, "\n\t/STATISTICS=");
+      ds_put_cstr (&dss, "\n\t/STATISTICS=");
       if (descriptives)
-       g_string_append (str, "DESCRIPTIVES ");
+       ds_put_cstr (&dss, "DESCRIPTIVES ");
       if (homogeneity)
-       g_string_append (str, "HOMOGENEITY ");
+       ds_put_cstr (&dss, "HOMOGENEITY ");
     }
 
   for (i = 0 ; i < ow->contrasts_array->len ; ++i )
@@ -250,7 +251,7 @@ static gchar * generate_syntax (const struct oneway_anova_dialog *ow)
       GtkTreeIter iter;
       gboolean ok;
 
-      g_string_append (str, "\n\t/CONTRAST=");
+      ds_put_cstr (&dss, "\n\t/CONTRAST=");
 
       for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(ls),
                                               &iter);
@@ -261,14 +262,14 @@ static gchar * generate_syntax (const struct oneway_anova_dialog *ow)
 
          gtk_tree_model_get (GTK_TREE_MODEL (ls), &iter, 0, &v, -1);
 
-         g_string_append_printf (str, " %g", v);
+         ds_put_c_format (&dss, " %g", v);
        }
     }
 
-  g_string_append (str, ".\n");
+  ds_put_cstr (&dss, ".\n");
 
-  text = str->str;
-  g_string_free (str, FALSE);
+  text = ds_steal_cstr (&dss);
+  ds_destroy (&dss);
 
   return text;
 }
index 97a5f3cfcc31d69e95db8742d152d79ef61fba40..019332eabad85ab651288a9443bdf26f04303bda 100644 (file)
@@ -137,26 +137,28 @@ generate_syntax (PsppireDialogAction *a)
   gchar *text = NULL;
 
   double prop;
-  GString *string = g_string_new ("NPAR TEST\n\t/BINOMIAL");
+  struct string str;
+
+  ds_init_cstr (&str, "NPAR TEST\n\t/BINOMIAL");
 
   if ( get_proportion (scd, &prop))
-    g_string_append_printf (string, "(%g)", prop);
+    ds_put_c_format (&str, "(%g)", prop);
 
-  g_string_append (string, " =");
+  ds_put_cstr (&str, " =");
 
-  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (scd->var_view), 0, string);
+  psppire_var_view_append_names_str (PSPPIRE_VAR_VIEW (scd->var_view), 0, &str);
 
   if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (scd->cutpoint_button)))
     {
       const gchar *cutpoint = gtk_entry_get_text (GTK_ENTRY (scd->cutpoint_entry));
-      g_string_append_printf (string, "(%s)", cutpoint);
+      ds_put_c_format  (&str, "(%s)", cutpoint);
     }
 
-  g_string_append (string, ".\n");
+  ds_put_cstr (&str, ".\n");
 
-  text = string->str;
+  text = ds_steal_cstr (&str);
 
-  g_string_free (string, FALSE);
+  ds_destroy (&str);
 
   return text;
 }
index 6b6144a6fb70559928cd8e3831d7f98396940799..2c6b3e63b3882dbebe273838e1e2cfc84ce94d41 100644 (file)
@@ -26,6 +26,7 @@
 #include "psppire-dialog.h"
 #include "builder-wrapper.h"
 #include "psppire-scanf.h"
+#include <libpspp/str.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -58,69 +59,68 @@ generate_syntax (PsppireDialogAction *act)
   PsppireDialogActionFactor *rd  = PSPPIRE_DIALOG_ACTION_FACTOR (act);
 
   gchar *text = NULL;
-  GString *string = g_string_new ("FACTOR ");
+  struct string str;
+  ds_init_cstr (&str, "FACTOR ");
 
-  g_string_append (string, "\n\t/VARIABLES=");
+  ds_put_cstr (&str, "\n\t/VARIABLES=");
 
-  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (rd->variables), 0, string);
+  psppire_var_view_append_names_str (PSPPIRE_VAR_VIEW (rd->variables), 0, &str);
 
 
-  g_string_append (string, "\n\t/CRITERIA = ");
+  ds_put_cstr (&str, "\n\t/CRITERIA = ");
   if ( rd->extraction.explicit_nfactors )
-    g_string_append_printf (string, "FACTORS (%d)", rd->extraction.n_factors);
+    ds_put_c_format (&str, "FACTORS (%d)", rd->extraction.n_factors);
   else
-    g_string_append_printf (string, "MINEIGEN (%g)", rd->extraction.mineigen);
+    ds_put_c_format (&str, "MINEIGEN (%g)", rd->extraction.mineigen);
 
   /*
     The CRITERIA = ITERATE subcommand is overloaded.
      It applies to the next /ROTATION and/or EXTRACTION command whatever comes first.
   */
-  g_string_append_printf (string, " ITERATE (%d)", rd->extraction.n_iterations);
+  ds_put_c_format (&str, " ITERATE (%d)", rd->extraction.n_iterations);
 
 
-  g_string_append (string, "\n\t/EXTRACTION =");
+  ds_put_cstr (&str, "\n\t/EXTRACTION =");
   if ( rd->extraction.paf)
-    g_string_append (string, "PAF");
+    ds_put_cstr (&str, "PAF");
   else
-    g_string_append (string, "PC");
+    ds_put_cstr (&str, "PC");
 
 
-  g_string_append (string, "\n\t/METHOD = ");
+  ds_put_cstr (&str, "\n\t/METHOD = ");
   if ( rd->extraction.covariance )
-    g_string_append (string, "COVARIANCE");
+    ds_put_cstr (&str, "COVARIANCE");
   else
-    g_string_append (string, "CORRELATION");
-
-
+    ds_put_cstr (&str, "CORRELATION");
 
   if ( rd->extraction.scree )
     {
-      g_string_append (string, "\n\t/PLOT = ");
-      g_string_append (string, "EIGEN");
+      ds_put_cstr (&str, "\n\t/PLOT = ");
+      ds_put_cstr (&str, "EIGEN");
     }
 
-  g_string_append (string, "\n\t/PRINT = ");
-  g_string_append (string, "INITIAL ");
+  ds_put_cstr (&str, "\n\t/PRINT = ");
+  ds_put_cstr (&str, "INITIAL ");
 
   if ( rd->extraction.unrotated )  
-    g_string_append (string, "EXTRACTION ");
+    ds_put_cstr (&str, "EXTRACTION ");
 
   if ( rd->rotation.rotated_solution )
-    g_string_append (string, "ROTATION");
+    ds_put_cstr (&str, "ROTATION");
 
 
   /* The CRITERIA = ITERATE subcommand is overloaded.
      It applies to the next /ROTATION and/or EXTRACTION command whatever comes first.
   */
-  g_string_append_printf (string, "\n\t/CRITERIA = ITERATE (%d)",  rd->rotation.iterations  );
+  ds_put_c_format (&str, "\n\t/CRITERIA = ITERATE (%d)",  rd->rotation.iterations  );
 
-  g_string_append (string, "\n\t/ROTATION = ");
-  g_string_append (string, rot_method_syntax[rd->rotation.method]);
+  ds_put_cstr (&str, "\n\t/ROTATION = ");
+  ds_put_cstr (&str, rot_method_syntax[rd->rotation.method]);
 
-  g_string_append (string, ".");
-  text = string->str;
+  ds_put_cstr (&str, ".");
+  text = ds_steal_cstr (&str);
 
-  g_string_free (string, FALSE);
+  ds_destroy (&str);
 
   return text;
 }
index b2ddd196a3cd5b413c5626ca1896165086246ab6..6f8049009eb5524361e167854042d3797fed555c 100644 (file)
@@ -350,44 +350,45 @@ generate_syntax (PsppireDialogAction * a)
   gboolean ok;
   GtkTreeIter iter;
   guint selected = 0;
+  struct string str;
 
-  GString *string = g_string_new ("FREQUENCIES");
+  ds_init_cstr (&str, "FREQUENCIES");
 
-  g_string_append (string, "\n\t/VARIABLES=");
-  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (fd->stat_vars), 0, string);
+  ds_put_cstr (&str, "\n\t/VARIABLES=");
+  psppire_var_view_append_names_str (PSPPIRE_VAR_VIEW (fd->stat_vars), 0, &str);
 
-  g_string_append (string, "\n\t/FORMAT=");
+  ds_put_cstr (&str, "\n\t/FORMAT=");
 
   switch (fd->tables_opts_order)
     {
     case FRQ_AVALUE:
-      g_string_append (string, "AVALUE");
+      ds_put_cstr (&str, "AVALUE");
       break;
     case FRQ_DVALUE:
-      g_string_append (string, "DVALUE");
+      ds_put_cstr (&str, "DVALUE");
       break;
     case FRQ_ACOUNT:
-      g_string_append (string, "AFREQ");
+      ds_put_cstr (&str, "AFREQ");
       break;
     case FRQ_DCOUNT:
-      g_string_append (string, "DFREQ");
+      ds_put_cstr (&str, "DFREQ");
       break;
     default:
       g_assert_not_reached ();
     }
 
-  g_string_append (string, " ");
+  ds_put_cstr (&str, " ");
 
   switch (fd->tables_opts_table)
     {
     case FRQ_TABLE:
-      g_string_append (string, "TABLE");
+      ds_put_cstr (&str, "TABLE");
       break;
     case FRQ_NOTABLE:
-      g_string_append (string, "NOTABLE");
+      ds_put_cstr (&str, "NOTABLE");
       break;
     case FRQ_LIMIT:
-      g_string_append_printf (string, "LIMIT (%d)", fd->tables_opts_limit);
+      ds_put_c_format (&str, "LIMIT (%d)", fd->tables_opts_limit);
       break;
     }
 
@@ -404,17 +405,17 @@ generate_syntax (PsppireDialogAction * a)
 
   if (selected != B_FS_DEFAULT)
     {
-      g_string_append (string, "\n\t/STATISTICS=");
+      ds_put_cstr (&str, "\n\t/STATISTICS=");
       if (selected == B_FS_ALL)
-        g_string_append (string, "ALL");
+        ds_put_cstr (&str, "ALL");
       else if (selected == 0)
-        g_string_append (string, "NONE");
+        ds_put_cstr (&str, "NONE");
       else
         {
           int n = 0;
           if ((selected & B_FS_DEFAULT) == B_FS_DEFAULT)
             {
-              g_string_append (string, "DEFAULT");
+              ds_put_cstr (&str, "DEFAULT");
               selected &= ~B_FS_DEFAULT;
               n++;
             }
@@ -422,49 +423,49 @@ generate_syntax (PsppireDialogAction * a)
             if (selected & (1u << i))
               {
                 if (n++)
-                  g_string_append (string, " ");
-                g_string_append (string, stats[i].name);
+                  ds_put_cstr (&str, " ");
+                ds_put_cstr (&str, stats[i].name);
               }
         }
     }
 
   if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (fd->include_missing)))
-    g_string_append (string, "\n\t/MISSING=INCLUDE");
+    ds_put_cstr (&str, "\n\t/MISSING=INCLUDE");
 
 
   if (fd->charts_opts_draw_hist)
     {
-      g_string_append (string, "\n\t/HISTOGRAM=");
-      g_string_append (string,
+      ds_put_cstr (&str, "\n\t/HISTOGRAM=");
+      ds_put_cstr (&str,
                        fd->charts_opts_draw_normal ? "NORMAL" : "NONORMAL");
 
       if (fd->charts_opts_scale == FRQ_PERCENT)
-        g_string_append (string, " PERCENT");
+        ds_put_cstr (&str, " PERCENT");
 
       if (fd->charts_opts_use_min)
-        g_string_append_printf (string, " MIN(%.15g)", fd->charts_opts_min);
+        ds_put_c_format (&str, " MIN(%.15g)", fd->charts_opts_min);
       if (fd->charts_opts_use_max)
-        g_string_append_printf (string, " MAX(%.15g)", fd->charts_opts_max);
+        ds_put_c_format (&str, " MAX(%.15g)", fd->charts_opts_max);
     }
 
   if (fd->charts_opts_draw_pie)
     {
-      g_string_append (string, "\n\t/PIECHART=");
+      ds_put_cstr (&str, "\n\t/PIECHART=");
 
       if (fd->charts_opts_pie_include_missing)
-        g_string_append (string, " MISSING");
+        ds_put_cstr (&str, " MISSING");
 
       if (fd->charts_opts_use_min)
-        g_string_append_printf (string, " MIN(%.15g)", fd->charts_opts_min);
+        ds_put_c_format (&str, " MIN(%.15g)", fd->charts_opts_min);
       if (fd->charts_opts_use_max)
-        g_string_append_printf (string, " MAX(%.15g)", fd->charts_opts_max);
+        ds_put_c_format (&str, " MAX(%.15g)", fd->charts_opts_max);
     }
 
-  g_string_append (string, ".\n");
+  ds_put_cstr (&str, ".\n");
 
-  text = string->str;
+  text = ds_steal_cstr (&str);
 
-  g_string_free (string, FALSE);
+  ds_destroy (&str);
 
   return text;
 }
index 0426931e85231e10ee2da445e3c6c5f831be9003..85137868c624cc9b854a036015f48dcd4395e417 100644 (file)
@@ -153,37 +153,38 @@ generate_syntax (PsppireDialogAction *a)
 {
   PsppireDialogActionLogistic *rd = PSPPIRE_DIALOG_ACTION_LOGISTIC (a);
   gchar *text = NULL;
+  struct string str;
+  const gchar *dep = gtk_entry_get_text (GTK_ENTRY (rd->dep_var));
 
-  GString *string = g_string_new ("LOGISTIC REGRESSION ");
+  ds_init_cstr (&str, "LOGISTIC REGRESSION ");
 
-  const gchar *dep = gtk_entry_get_text (GTK_ENTRY (rd->dep_var));
+  ds_put_cstr (&str, dep);
 
-  g_string_append (string, dep);
+  ds_put_cstr (&str, " WITH ");
 
-  g_string_append (string, " WITH ");
+  psppire_var_view_append_names_str (PSPPIRE_VAR_VIEW (rd->indep_vars), 0, &str);
 
-  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (rd->indep_vars), 0, string);
+  ds_put_cstr (&str, "\n\t/CRITERIA =");
 
-  g_string_append (string, "\n\t/CRITERIA =");
+  syntax_gen_pspp (&str, " CUT(%g)", rd->cut_point);
 
-  g_string_append_printf (string, " CUT(%g)", rd->cut_point);
-  g_string_append_printf (string, " ITERATE(%d)", rd->max_iterations);
+  syntax_gen_pspp (&str, " ITERATE(%d)", rd->max_iterations);
 
   if (rd->conf)
     {
-      g_string_append_printf (string, "\n\t/PRINT = CI(%g)", rd->conf_level);
+      syntax_gen_pspp (&str, "\n\t/PRINT = CI(%g)", rd->conf_level);
     }
 
   if (rd->constant) 
-    g_string_append (string, "\n\t/NOORIGIN");
+    ds_put_cstr (&str, "\n\t/NOORIGIN");
   else
-    g_string_append (string, "\n\t/ORIGIN");
+    ds_put_cstr (&str, "\n\t/ORIGIN");
 
-  g_string_append (string, ".\n");
+  ds_put_cstr (&str, ".\n");
 
-  text = string->str;
+  text = ds_steal_cstr (&str);
 
-  g_string_free (string, FALSE);
+  ds_destroy (&str);
 
   return text;
 }
index 61f3bbdd28f70c28dc4fd54a910b21e08a0cce09..d1d7e89166154b849352028d9858ab1a5ab4267b 100644 (file)
@@ -41,6 +41,7 @@
 #include "gl/error.h"
 #include "gl/tmpdir.h"
 #include "gl/xalloc.h"
+#include "gl/c-xvasprintf.h"
 
 #include "helper.h"
 
@@ -1049,7 +1050,6 @@ psppire_output_window_new (void)
                                   NULL));
 }
 
-
 \f
 static void
 create_xr_print_driver (GtkPrintContext *context, PsppireOutputWindow *window)
@@ -1072,15 +1072,15 @@ create_xr_print_driver (GtkPrintContext *context, PsppireOutputWindow *window)
 
   string_map_init (&options);
   string_map_insert_nocopy (&options, xstrdup ("paper-size"),
-                            xasprintf("%.2fx%.2fmm", width, height));
+                            c_xasprintf("%.2fx%.2fmm", width, height));
   string_map_insert_nocopy (&options, xstrdup ("left-margin"),
-                            xasprintf ("%.2fmm", left_margin));
+                            c_xasprintf ("%.2fmm", left_margin));
   string_map_insert_nocopy (&options, xstrdup ("right-margin"),
-                            xasprintf ("%.2fmm", right_margin));
+                            c_xasprintf ("%.2fmm", right_margin));
   string_map_insert_nocopy (&options, xstrdup ("top-margin"),
-                            xasprintf ("%.2fmm", top_margin));
+                            c_xasprintf ("%.2fmm", top_margin));
   string_map_insert_nocopy (&options, xstrdup ("bottom-margin"),
-                            xasprintf ("%.2fmm", bottom_margin));
+                            c_xasprintf ("%.2fmm", bottom_margin));
 
   window->print_xrd =
     xr_driver_create (gtk_print_context_get_cairo_context (context), &options);
index 01d694e27e7e2fc98808488e27d0c603aecbe05e..eb233b4ae5d2ed87fd1f536b8e85c3252c90af95 100644 (file)
@@ -543,46 +543,46 @@ old_value_get_type (void)
 
 /* Generate a syntax fragment for NV and append it to STR */
 void
-old_value_append_syntax (GString *str, const struct old_value *ov)
+old_value_append_syntax (struct string *str, const struct old_value *ov)
 {
   switch (ov->type)
     {
     case OV_NUMERIC:
-      g_string_append_printf (str, "%g", ov->v.v);
+      ds_put_c_format (str, "%g", ov->v.v);
       break;
     case OV_STRING:
       {
        struct string ds = DS_EMPTY_INITIALIZER;
        syntax_gen_string (&ds, ss_cstr (ov->v.s));
-       g_string_append (str, ds_cstr (&ds));
+       ds_put_cstr (str, ds_cstr (&ds));
        ds_destroy (&ds);
       }
       break;
     case OV_MISSING:
-      g_string_append (str, "MISSING");
+      ds_put_cstr (str, "MISSING");
       break;
     case OV_SYSMIS:
-      g_string_append (str, "SYSMIS");
+      ds_put_cstr (str, "SYSMIS");
       break;
     case OV_ELSE:
-      g_string_append (str, "ELSE");
+      ds_put_cstr (str, "ELSE");
       break;
     case OV_RANGE:
-      g_string_append_printf (str, "%g THRU %g",
+      ds_put_c_format (str, "%g THRU %g",
                              ov->v.range[0],
                              ov->v.range[1]);
       break;
     case OV_LOW_UP:
-      g_string_append_printf (str, "LOWEST THRU %g",
+      ds_put_c_format (str, "LOWEST THRU %g",
                              ov->v.range[1]);
       break;
     case OV_HIGH_DOWN:
-      g_string_append_printf (str, "%g THRU HIGHEST",
+      ds_put_c_format (str, "%g THRU HIGHEST",
                              ov->v.range[0]);
       break;
     default:
       g_warning ("Invalid type in old recode value");
-      g_string_append (str, "???");
+      ds_put_cstr (str, "???");
       break;
     };
 }
index 5c8175bae39c69f2744d83605613201cd0fdec14..0f487a45db1155d4c4dc8c6aaff7061dd1d1bd36 100644 (file)
@@ -95,7 +95,8 @@ struct old_value
 
 GType old_value_get_type (void);
 
-void old_value_append_syntax (GString *str, const struct old_value *ov);
+struct string;
+void old_value_append_syntax (struct string *str, const struct old_value *ov);
 
 void psppire_val_chooser_get_status (PsppireValChooser *vr, struct old_value *ov);
 void psppire_val_chooser_set_status (PsppireValChooser *vr, const struct old_value *ov);
index 5ef5b86dac3d706b6e2864b215d2dc1ef25b2d30..c71a5e66103e2821814972461c12b46e1de0aa5a 100644 (file)
@@ -21,6 +21,7 @@
 #include "psppire-var-ptr.h"
 #include "psppire-select-dest.h"
 
+#include <libpspp/str.h>
 #include <data/variable.h>
 
 #include <gettext.h>
@@ -400,3 +401,33 @@ psppire_var_view_append_names (PsppireVarView *vv, gint column, GString *string)
 
   return n_vars;
 }
+
+
+/*
+  Append the names of selected variables to STR
+  Returns the number of variables appended.
+*/
+gint 
+psppire_var_view_append_names_str (PsppireVarView *vv, gint column, struct string *str)
+{
+  gint n_vars = 0;
+  GtkTreeIter iter;
+
+  if ( psppire_var_view_get_iter_first (vv, &iter) )
+    {
+      do
+       {
+         const struct variable *var = psppire_var_view_get_variable (vv, column, &iter);
+         ds_put_cstr (str, " ");
+         ds_put_cstr (str, var_get_name (var));
+
+         n_vars++;
+       }
+      while (psppire_var_view_get_iter_next (vv, &iter));
+    }
+
+  return n_vars;
+}
+
+
+
index 090966b526bdbc830d5a48b905658e2ba97d0547..a8fb0565cab64fbcb2bfd1f0f902621451f99d70 100644 (file)
@@ -64,6 +64,8 @@ struct _PsppireVarViewClass
 GType      psppire_var_view_get_type        (void);
 
 gint psppire_var_view_append_names (PsppireVarView *vv, gint column, GString *string);
+struct string;
+gint psppire_var_view_append_names_str (PsppireVarView *vv, gint column, struct string *);
 
 gboolean psppire_var_view_get_iter_first (PsppireVarView *vv, GtkTreeIter *iter);
 
index 05984dd44b368af9684b167fd66b0420ff0881f4..f8d21336e131bc4d46bc832eef937c11f28e69dc 100644 (file)
@@ -951,31 +951,26 @@ run_old_and_new_dialog (struct recode_dialog *rd)
 
 /* Generate a syntax fragment for NV and append it to STR */
 static void
-new_value_append_syntax (GString *str, const struct new_value *nv)
+new_value_append_syntax (struct string *dds, const struct new_value *nv)
 {
   switch (nv->type)
     {
     case NV_NUMERIC:
-      g_string_append_printf (str, "%g", nv->v.v);
+      ds_put_c_format (dds, "%g", nv->v.v);
       break;
     case NV_STRING:
-      {
-       struct string ds = DS_EMPTY_INITIALIZER;
-       syntax_gen_string (&ds, ss_cstr (nv->v.s));
-       g_string_append (str, ds_cstr (&ds));
-       ds_destroy (&ds);
-      }
+      syntax_gen_string (dds, ss_cstr (nv->v.s));
       break;
     case NV_COPY:
-      g_string_append (str, "COPY");
+      ds_put_cstr (dds, "COPY");
       break;
     case NV_SYSMIS:
-      g_string_append (str, "SYSMIS");
+      ds_put_cstr (dds, "SYSMIS");
       break;
     default:
       /* Shouldn't ever happen */
       g_warning ("Invalid type in new recode value");
-      g_string_append (str, "???");
+      ds_put_cstr (dds, "???");
       break;
     }
 }
@@ -987,8 +982,10 @@ generate_syntax (const struct recode_dialog *rd)
   gboolean ok;
   GtkTreeIter iter;
   gchar *text;
+  struct string dds;
+
+  ds_init_empty (&dds);
 
-  GString *str = g_string_sized_new (100);
 
   /* Declare new string variables if applicable */
   if ( rd->different &&
@@ -1002,24 +999,24 @@ generate_syntax (const struct recode_dialog *rd)
       g_hash_table_iter_init (&iter, rd->varmap);
       while (g_hash_table_iter_next (&iter, (void**) &var, (void**) &nlp))
        {
-         g_string_append (str, "\nSTRING ");
-         g_string_append (str, nlp->name);
-         g_string_append_printf (str, " (A%d).",
+         ds_put_cstr (&dds, "\nSTRING ");
+         ds_put_cstr (&dds, nlp->name);
+         ds_put_c_format (&dds, " (A%d).",
                                  (int)
                                  gtk_spin_button_get_value (GTK_SPIN_BUTTON (rd->width_entry) )
                                  );
        }
     }
 
-  g_string_append (str, "\nRECODE ");
+  ds_put_cstr (&dds, "\nRECODE ");
 
-  psppire_var_view_append_names (PSPPIRE_VAR_VIEW (rd->variable_treeview), 0, str);
+  psppire_var_view_append_names_str (PSPPIRE_VAR_VIEW (rd->variable_treeview), 0, &dds);
 
-  g_string_append (str, "\n\t");
+  ds_put_cstr (&dds, "\n\t");
 
   if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->convert_button)))
     {
-      g_string_append (str, "(CONVERT) ");
+      ds_put_cstr (&dds, "(CONVERT) ");
     }
 
   for (ok = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (rd->value_map),
@@ -1040,13 +1037,13 @@ generate_syntax (const struct recode_dialog *rd)
       ov = g_value_get_boxed (&ov_value);
       nv = g_value_get_boxed (&nv_value);
 
-      g_string_append (str, "(");
+      ds_put_cstr (&dds, "(");
 
-      old_value_append_syntax (str, ov);
-      g_string_append (str, " = ");
-      new_value_append_syntax (str, nv);
+      old_value_append_syntax (&dds, ov);
+      ds_put_cstr (&dds, " = ");
+      new_value_append_syntax (&dds, nv);
 
-      g_string_append (str, ") ");
+      ds_put_cstr (&dds, ") ");
       g_value_unset (&ov_value);
       g_value_unset (&nv_value);
     }
@@ -1056,7 +1053,7 @@ generate_syntax (const struct recode_dialog *rd)
     {
 
       GtkTreeIter iter;
-      g_string_append (str, "\n\tINTO ");
+      ds_put_cstr (&dds, "\n\tINTO ");
 
       for (ok = psppire_var_view_get_iter_first (PSPPIRE_VAR_VIEW (rd->variable_treeview), &iter);
           ok;
@@ -1067,12 +1064,12 @@ generate_syntax (const struct recode_dialog *rd)
 
            nlp = g_hash_table_lookup (rd->varmap, var);
            
-           g_string_append (str, nlp->name);
-           g_string_append (str, " ");
+           ds_put_cstr (&dds, nlp->name);
+           ds_put_cstr (&dds, " ");
          }
     }
 
-  g_string_append (str, ".");
+  ds_put_cstr (&dds, ".");
 
   /* If applicable, set labels for the new variables. */
   if ( rd->different )
@@ -1090,7 +1087,7 @@ generate_syntax (const struct recode_dialog *rd)
              struct string sl;
              ds_init_empty (&sl);
              syntax_gen_string (&sl, ss_cstr (nlp->label));
-             g_string_append_printf (str, "\nVARIABLE LABELS %s %s.",
+             ds_put_c_format (&dds, "\nVARIABLE LABELS %s %s.",
                                      nlp->name, ds_cstr (&sl));
 
              ds_destroy (&sl);
@@ -1098,12 +1095,12 @@ generate_syntax (const struct recode_dialog *rd)
        }
     }
 
-  g_string_append (str, "\nEXECUTE.\n");
+  ds_put_cstr (&dds, "\nEXECUTE.\n");
 
 
-  text = str->str;
+  text = ds_steal_cstr (&dds);
 
-  g_string_free (str, FALSE);
+  ds_destroy (&dds);
 
   return text;
 }
index c03906a6a8a2fedec3c5301864a9bf2224bd430b..c3bc123c051f2fd42cc8a121ba316056ee148706 100644 (file)
@@ -346,12 +346,14 @@ static gchar *
 generate_syntax_filter (const struct select_cases_dialog *scd)
 {
   gchar *text = NULL;
-  GString *string = g_string_new ("");
+  struct string dss;
 
   const gchar *filter = "filter_$";
   const gchar key[]="case_$";
 
-  if ( gtk_toggle_button_get_active
+  ds_init_empty (&dss);
+
+  if (gtk_toggle_button_get_active
        (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
                                              "radiobutton-range"))))
     {
@@ -363,14 +365,14 @@ generate_syntax_filter (const struct select_cases_dialog *scd)
        GTK_SPIN_BUTTON (get_widget_assert (scd->xml,
                                           "range-dialog-last"));
 
-      g_string_append_printf (string,
+      ds_put_c_format (&dss,
                              "COMPUTE filter_$ = ($CASENUM >= %ld "
                               "AND $CASENUM <= %ld).\n",
                              (long) gtk_spin_button_get_value (first),
                              (long) gtk_spin_button_get_value (last)
                              );
 
-      g_string_append (string, "EXECUTE.\n");
+      ds_put_cstr (&dss, "EXECUTE.\n");
     }
   else if ( gtk_toggle_button_get_active
        (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
@@ -385,7 +387,7 @@ generate_syntax_filter (const struct select_cases_dialog *scd)
          const double percentage =
            gtk_spin_button_get_value (GTK_SPIN_BUTTON (scd->spinbutton));
 
-         g_string_append_printf (string,
+         ds_put_c_format (&dss,
                                  "COMPUTE %s = RV.UNIFORM (0,1) < %g.\n",
                                  filter,
                                  percentage / 100.0 );
@@ -400,49 +402,47 @@ generate_syntax_filter (const struct select_cases_dialog *scd)
 
          const gchar ranvar[]="rv_$";
 
-         g_string_append_printf (string,
+         ds_put_c_format (&dss,
                                  "COMPUTE %s = $CASENUM.\n", key);
 
-         g_string_append_printf (string,
+         ds_put_c_format (&dss,
                                  "COMPUTE %s = %s > %d.\n",
                                  filter, key, from_n_cases);
 
-         g_string_append_printf (string,
+         ds_put_c_format (&dss,
                                  "COMPUTE %s = RV.UNIFORM (0, 1).\n",
                                  ranvar);
 
-         g_string_append_printf (string,
+         ds_put_c_format (&dss,
                                  "SORT BY %s, %s.\n",
                                  filter, ranvar);
 
-         g_string_append (string, "EXECUTE.\n");
+         ds_put_cstr (&dss, "EXECUTE.\n");
                                  
 
-         g_string_append_printf (string,
+         ds_put_c_format (&dss,
                                  "COMPUTE %s = $CASENUM.\n",
                                  filter );
 
-         g_string_append_printf (string,
+         ds_put_c_format (&dss,
                                  "COMPUTE %s = %s <= %d\n",
                                  filter,
                                  filter,
                                  n_cases );
 
-         g_string_append (string, "EXECUTE.\n");
+         ds_put_cstr (&dss, "EXECUTE.\n");
 
 
-         g_string_append_printf (string,
+         ds_put_c_format (&dss,
                                  "SORT BY %s.\n",
                                  key);
 
-         g_string_append_printf (string,
+         ds_put_c_format (&dss,
                                  "DELETE VARIABLES %s, %s.\n",
                                  key, ranvar);
-
        }
 
-      g_string_append (string, "EXECUTE.\n");
-
+      ds_put_cstr (&dss, "EXECUTE.\n");
     }
   else
     {
@@ -452,11 +452,12 @@ generate_syntax_filter (const struct select_cases_dialog *scd)
       filter = gtk_entry_get_text (entry);
     }
 
+  ds_put_c_format (&dss, "FILTER BY %s.\n", filter);
+
+  text  = ds_steal_cstr (&dss);
 
-  g_string_append_printf  (string, "FILTER BY %s.\n", filter);
+  ds_destroy (&dss);
 
-  text  = string->str;
-  g_string_free (string, FALSE);
   return text;
 }
 
@@ -464,7 +465,7 @@ static gchar *
 generate_syntax_delete (const struct select_cases_dialog *scd)
 {
   gchar *text = NULL;
-  GString *string = NULL;
+  struct string dss;
 
   if ( gtk_toggle_button_get_active
        (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
@@ -473,7 +474,7 @@ generate_syntax_delete (const struct select_cases_dialog *scd)
       return xstrdup ("\n");
     }
 
-  string = g_string_new ("");
+  ds_init_empty (&dss);
 
   if ( gtk_toggle_button_get_active
        (GTK_TOGGLE_BUTTON (get_widget_assert (scd->xml,
@@ -483,13 +484,13 @@ generate_syntax_delete (const struct select_cases_dialog *scd)
       get_widget_assert (scd->xml,
                         "radiobutton-sample-percent");
 
-    g_string_append (string, "SAMPLE ");
+    ds_put_cstr (&dss, "SAMPLE ");
 
     if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (random_sample)))
       {
        const double percentage =
          gtk_spin_button_get_value (GTK_SPIN_BUTTON (scd->spinbutton));
-       g_string_append_printf (string, "%g.", percentage / 100.0);
+       ds_put_c_format (&dss, "%g.", percentage / 100.0);
       }
     else
       {
@@ -498,7 +499,7 @@ generate_syntax_delete (const struct select_cases_dialog *scd)
        const gint from_n_cases =
          gtk_spin_button_get_value (GTK_SPIN_BUTTON (scd->spinbutton2));
 
-       g_string_append_printf (string, "%d FROM %d .", n_cases, from_n_cases);
+       ds_put_c_format (&dss, "%d FROM %d .", n_cases, from_n_cases);
       }
 
   }
@@ -514,14 +515,14 @@ generate_syntax_delete (const struct select_cases_dialog *scd)
        GTK_SPIN_BUTTON (get_widget_assert (scd->xml,
                                           "range-dialog-last"));
 
-      g_string_append_printf (string,
+      ds_put_c_format (&dss,
                              "COMPUTE filter_$ = ($CASENUM >= %ld "
                               "AND $CASENUM <= %ld).\n",
                              (long) gtk_spin_button_get_value (first),
                              (long) gtk_spin_button_get_value (last)
                              );
-      g_string_append (string, "EXECUTE.\n");
-      g_string_append_printf (string, "SELECT IF filter_$.\n");
+      ds_put_cstr (&dss, "EXECUTE.\n");
+      ds_put_c_format (&dss, "SELECT IF filter_$.\n");
 
     }
   else if ( gtk_toggle_button_get_active
@@ -533,17 +534,17 @@ generate_syntax_delete (const struct select_cases_dialog *scd)
        GTK_ENTRY (get_widget_assert (scd->xml,
                                      "filter-variable-entry"));
 
-      g_string_append_printf (string, "SELECT IF (%s <> 0).",
+      ds_put_c_format (&dss, "SELECT IF (%s <> 0).",
                              gtk_entry_get_text (entry));
     }
 
 
-  g_string_append (string, "\n");
+  ds_put_cstr (&dss, "\n");
 
+  text = ds_steal_cstr (&dss);
 
+  ds_destroy (&dss);
 
-  text  = string->str;
-  g_string_free (string, FALSE);
   return text;
 }
 
index 671eba0c32c18b0a19fded5b9a7281c0a519a8fa..467d4321ff00a416721cb9cecd17c3b935ff7599 100644 (file)
@@ -127,14 +127,20 @@ tt_options_dialog_run (struct tt_options_dialog *tto)
 void
 tt_options_dialog_append_syntax (const struct tt_options_dialog *tto, GString *str)
 {
-  g_string_append (str, "\t/MISSING=");
+  struct string dss;
+  ds_init_empty (&dss);
 
-  if ( tto->excl == EXCL_ANALYSIS )
-    g_string_append (str, "ANALYSIS");
+  ds_put_cstr (&dss, "\t/MISSING=");
+
+  if (tto->excl == EXCL_ANALYSIS)
+    ds_put_cstr (&dss, "ANALYSIS");
   else
-    g_string_append (str, "LISTWISE");
+    ds_put_cstr (&dss, "LISTWISE");
+
+  ds_put_c_format (&dss, "\n\t/CRITERIA=CIN(%g)",
+                  tto->confidence_interval/100.0);
 
+  g_string_append (str, ds_cstr (&dss));
 
-  g_string_append_printf (str, "\n\t/CRITERIA=CIN(%g)",
-                         tto->confidence_interval/100.0);
+  ds_destroy (&dss);
 }
index bcec18e5565bab56169eee50766d541c9f5e0999..dca3bd3c5b3cb778dde442553cebd40c83c23152 100644 (file)
@@ -30,6 +30,7 @@
 #include "libpspp/i18n.h"
 #include "libpspp/message.h"
 #include "libpspp/str.h"
+#include "libpspp/misc.h"
 
 #include "gl/ftoastr.h"
 
@@ -176,7 +177,7 @@ syntax_gen_number (struct string *output,
     {
       char s[DBL_BUFSIZE_BOUND];
 
-      dtoastr (s, sizeof s, 0, 0, number);
+      c_dtoastr (s, sizeof s, 0, 0, number);
       ds_put_cstr (output, s);
     }
 }
@@ -231,6 +232,7 @@ syntax_gen_pspp_valist (struct string *output, const char *format,
 {
   for (;;)
     {
+      char directive;
       size_t copy = strcspn (format, "%");
       ds_put_substring (output, ss_buffer (format, copy));
       format += copy;
@@ -239,7 +241,8 @@ syntax_gen_pspp_valist (struct string *output, const char *format,
         return;
       assert (*format == '%');
       format++;
-      switch (*format++)
+      directive = *format++;
+      switch (directive)
         {
         case 's':
           {
@@ -266,16 +269,14 @@ syntax_gen_pspp_valist (struct string *output, const char *format,
           break;
 
         case 'f':
+       case 'g':
           {
+           char conv[3];
             double d = va_arg (args, double);
-            switch (*format++)
-              {
-              case 'p':
-                ds_put_format (output, "%f", d);
-                break;
-              default:
-                NOT_REACHED ();
-              }
+           conv[0]='%';
+           conv[1]=directive;
+           conv[2]='\0';
+           ds_put_c_format (output, conv, d);
             break;
           }
 
index 6c4cef20aa082442cf7590bf820c62665a56c79b..737df306f84dde79e03caefe749b5d1b7bb4ce73 100644 (file)
@@ -238,6 +238,7 @@ TESTSUITE_AT = \
        tests/data/data-in.at \
        tests/data/data-out.at \
        tests/data/datasheet-test.at \
+       tests/data/dictionary.at \
        tests/data/format-guesser.at \
        tests/data/por-file.at \
        tests/data/sys-file-reader.at \
diff --git a/tests/data/dictionary.at b/tests/data/dictionary.at
new file mode 100644 (file)
index 0000000..ef62c66
--- /dev/null
@@ -0,0 +1,14 @@
+AT_BANNER([dictionary])
+
+AT_SETUP([dictionary case-insensitivity])
+AT_DATA([dictionary.sps], [dnl
+DATA LIST LIST /aèiöu aeiou.
+BEGIN DATA
+1 2
+END DATA.
+LIST AÈIÖU
+RENAME VARIABLE (aèiöu=AÈIÖU).
+LIST.
+RENAME VARIABLE (aeiou=aèiöu).
+])
+AT_CLEANUP
index 9fd8c09e19e3c17ebf72b535947c8f563e124074..c5699b136a282045c1a70314fe32447e4943e4ea 100644 (file)
@@ -131,7 +131,7 @@ num8,Format: F8.0,,8
 ,Missing Values: 1 THRU 3; 5,,
 num9,Format: F8.0,,9
 ,Missing Values: 1 THRU HIGHEST; -5,,
-numÃ\80Ã\88Ã\8cÃ\91Ã\92,Format: F8.0,,10
+numàèìñò,Format: F8.0,,10
 ,Missing Values: LOWEST THRU 1; 5,,
 str1,Format: A4,,11
 str2,String variable 2's label,,12
@@ -151,7 +151,7 @@ str8,25-byte string,,18
 ,Format: A25,,
 
 Table: Data List
-num1,num2,num3,num4,num5,num6,num7,num8,num9,numÃ\80Ã\88Ã\8cÃ\91Ã\92,str1,str2,str3,str4,str5,str6,str7,str8
+num1,num2,num3,num4,num5,num6,num7,num8,num9,numàèìñò,str1,str2,str3,str4,str5,str6,str7,str8
 1,2,3,4,5,6,7,8,9,10,abcd,efgh,ijkl,mnop,qrst,uvwx,yzABCDEFGHI,JKLMNOPQRSTUVWXYZ01234567
 ])
 done
@@ -1014,11 +1014,11 @@ LIST.
   AT_CHECK([pspp -o pspp.csv sys-file.sps])
   AT_CHECK([grep -v Measure pspp.csv | grep -v Display], [0], [dnl
 Variable,Description,,Position
-sÃ\89q256,Format: A256,,1
+séq256,Format: A256,,1
 str600,Format: A600,,2
 
 Table: Data List
-sÃ\89q256,str600
+séq256,str600
 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@a,abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyz
 ])
 done
index 0a2ea2896f971317ac239eeb44161615522eef60..41ccf21043f3ff5d7f7df431b9420563149f227b 100644 (file)
@@ -17,7 +17,7 @@ m4_define([CHECK_UPDATE],
 ])
    AT_DATA([b.data], [dnl
 1bN
-3bO
+3 O
 4bP
 6bQ
 7bR
@@ -44,6 +44,7 @@ UPDATE
     BY a.
 LIST.
 ])
+   cat update.sps
    AT_CHECK([pspp -O format=csv update.sps], [0], [dnl
 update.sps:6: warning: UPDATE: Encountered 3 sets of duplicate cases in the master file.
 
@@ -53,7 +54,7 @@ a,b,c,d,InA,InB
 1,b,B,N,1,1
 1,a,C,,1,0
 2,a,D,,1,0
-3,b,E,O,1,1
+3,a,E,O,1,1
 4,b,F,P,1,1
 5,a,G,,1,0
 5,a,H,,1,0
index f5eb2a404cc803182e338251244afc2eabf67624..2e5d886f3fe05fdc53013d9f4a788e008df472c6 100644 (file)
@@ -1685,3 +1685,32 @@ Asymp. Sig. (2-tailed),1.000,.414
 
 AT_CLEANUP
 
+
+
+AT_SETUP([NPAR TESTS CHISQUARE crash])
+dnl This syntax had been observed to crash pspp
+
+AT_DATA([npar.sps], [dnl
+data list list /x *.
+begin data.
+1
+2
+3
+4
+5
+6
+7
+8
+9
+10
+end data.
+
+* This happens to be invalid syntax.  But should not crash.
+NPAR TEST
+       /CHISQUARE= x(0.098, 99.098)
+       /EXPECTED =  1.2.
+])
+
+AT_CHECK([pspp -O format=csv npar.sps], [1], [ignore])
+
+AT_CLEANUP
index a8dd9dd6b7068049a377a8f56c23b4282262a58a..3ae027fb36db78b5afaf6b28890810a1da26847b 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2008, 2009, 2010, 2012 Free Software Foundation, Inc.
 
    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
@@ -39,6 +39,7 @@
 
 #include "libpspp/hash-functions.h"
 #include "libpspp/compiler.h"
+#include "libpspp/i18n.h"
 #include "libpspp/str.h"
 #include "libpspp/string-set.h"
 #include "libpspp/stringi-set.h"
@@ -276,7 +277,7 @@ check_map_contains (struct stringi_map *map,
 
   node = stringi_map_find_node (map, key);
   check (node != NULL);
-  check (!strcasecmp (key, stringi_map_node_get_key (node)));
+  check (!utf8_strcasecmp (key, stringi_map_node_get_key (node)));
   check (!strcmp (value, stringi_map_node_get_value (node)));
 
   check (node == stringi_map_insert (map, key, "012"));
index a20e84ce185d9ab2087860bbe0a878d8a7cc2e74..daf7b2fa6d97a6cfaf382c5e1b71ffbfe5ce8024 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 2007, 2008, 2009, 2010, 2012 Free Software Foundation, Inc.
 
    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
@@ -36,6 +36,7 @@
 #include <string.h>
 
 #include "libpspp/compiler.h"
+#include "libpspp/i18n.h"
 #include "libpspp/str.h"
 \f
 /* Exit with a failure code.
@@ -244,7 +245,7 @@ check_set_contains (struct stringi_set *set, const char *string)
 
   node = stringi_set_find_node (set, string);
   check (node != NULL);
-  check (!strcasecmp (string, stringi_set_node_get_string (node)));
+  check (!utf8_strcasecmp (string, stringi_set_node_get_string (node)));
 }
 
 /* Checks that SET contains the CNT strings in DATA, that its structure is
@@ -302,7 +303,7 @@ check_stringi_set (struct stringi_set *set, const int data[], size_t cnt)
           check (s == array[i]);
 
           for (j = 0; j < left; j++)
-            if (!strcasecmp (s, make_string (data_copy[j])))
+            if (!utf8_strcasecmp (s, make_string (data_copy[j])))
               {
                 data_copy[j] = data_copy[--left];
                 goto next;
@@ -319,7 +320,7 @@ check_stringi_set (struct stringi_set *set, const int data[], size_t cnt)
       for (i = 0; i < cnt; i++)
         {
           if (i > 0)
-            check (strcasecmp (array[i - 1], array[i]) < 0);
+            check (utf8_strcasecmp (array[i - 1], array[i]) < 0);
           check (stringi_set_contains (set, array[i]));
         }
       free (array);
index 07b440efb591d797194f732666b952f2a23185d1..a3f66b933be8bb5afdaa570d7799b3282db5dfc6 100644 (file)
@@ -520,6 +520,47 @@ _\bà_\be_\bî_\bo̧_\bũ_\by
 ])
 AT_CLEANUP
 
+AT_SETUP([ASCII driver u8_line_set_length])
+AT_KEYWORDS([render rendering])
+AT_DATA([input], [dnl
+0 0 0 àéî
+0 1 0 àéî
+0 2 0 àéî
+0 3 0 àéî
+0 4 0 àéî
+set-length 0 4
+set-length 1 3
+set-length 2 2
+set-length 3 1
+set-length 4 0
+
+0 6 0 あい
+0 7 0 あい
+0 8 0 あい
+0 9 0 あい
+0 10 0 あい
+0 11 0 あい
+set-length 6 5
+set-length 7 4
+set-length 8 3
+set-length 9 2
+set-length 10 1
+set-length 11 0
+])
+AT_CHECK([render-test --draw-mode input], [0], [dnl
+àéî
+àéî
+àé
+à
+
+あい
+あい
+あ?
+あ
+?
+])
+AT_CLEANUP
+
 AT_SETUP([ASCII driver Unicode box characters])
 AT_KEYWORDS([render rendering])
 AT_DATA([input], [3 3
index 0376e5b49b08ed60ada321424f241ab54fdaeb7a..5f4c1da00bc5313d98558e9bfa702981f1929e3b 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
 
    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
@@ -361,6 +361,7 @@ draw (FILE *stream)
   while (fgets (buffer, sizeof buffer, stream))
     {
       char text[sizeof buffer];
+      int length;
       int emph;
       int x, y;
 
@@ -368,9 +369,11 @@ draw (FILE *stream)
       if (strchr ("#\r\n", buffer[0]))
         continue;
 
-      if (sscanf (buffer, "%d %d %d %[^\n]", &x, &y, &emph, text) != 4)
+      if (sscanf (buffer, "%d %d %d %[^\n]", &x, &y, &emph, text) == 4)
+        ascii_test_write (ascii_driver, text, x, y, emph ? TAB_EMPH : 0);
+      else if (sscanf (buffer, "set-length %d %d", &y, &length) == 2)
+        ascii_test_set_length (ascii_driver, y, length);
+      else
         error (1, 0, "line %d has invalid format", line);
-
-      ascii_test_write (ascii_driver, text, x, y, emph ? TAB_EMPH : 0);
     }
 }