From: Ben Pfaff Date: Sun, 30 Dec 2012 03:21:39 +0000 (-0800) Subject: Merge 'master' into 'psppsheet'. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fbuilds%2F20130102032118%2Fpspp;hp=fa1fffd5c789d9c7875fc3bdf556eaf017cf524e;p=pspp Merge 'master' into 'psppsheet'. --- diff --git a/README.Git b/README.Git index 98ea982427..33337ed2bb 100644 --- a/README.Git +++ b/README.Git @@ -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 - Date: Sun Jul 29 00:30:48 2012 +0200 + commit f022473fdaf724d84817c4003120b9a38fbf884b + Author: Ben Pfaff + 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 61235efd90..72ec791a5e 100644 --- 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 \ diff --git a/doc/data-io.texi b/doc/data-io.texi index da1712e3d4..88577a48e2 100644 --- a/doc/data-io.texi +++ b/doc/data-io.texi @@ -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 diff --git a/glade/acr.c b/glade/acr.c index 397dc5d812..c6ee0ef639 100644 --- a/glade/acr.c +++ b/glade/acr.c @@ -1,3 +1,4 @@ +#include #include #include diff --git a/glade/bbox.c b/glade/bbox.c index 46ed7fbd02..2e675c41b2 100644 --- a/glade/bbox.c +++ b/glade/bbox.c @@ -1,3 +1,4 @@ +#include #include #include diff --git a/glade/dialog.c b/glade/dialog.c index 09330bd787..7b13226e01 100644 --- a/glade/dialog.c +++ b/glade/dialog.c @@ -1,3 +1,4 @@ +#include #include #include diff --git a/glade/selector.c b/glade/selector.c index d2b21a9972..6b2e4d7938 100644 --- a/glade/selector.c +++ b/glade/selector.c @@ -1,3 +1,4 @@ +#include #include #include diff --git a/po/de.po b/po/de.po index 84371ee6c3..d9283e95c7 100644 --- 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 , 2012. +# Matthias Keil , - 2012 +# Stefan Grotz , 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 \n" +"PO-Revision-Date: 2012-12-17 19:01+0100\n" +"Last-Translator: Matthias Keil \n" "Language-Team: German \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..." diff --git a/src/data/attributes.c b/src/data/attributes.c index d262e2ebc3..c4ae97c285 100644 --- a/src/data/attributes.c +++ b/src/data/attributes.c @@ -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 diff --git a/src/data/csv-file-writer.c b/src/data/csv-file-writer.c index cbc617b3d2..4ebff0bd00 100644 --- a/src/data/csv-file-writer.c +++ b/src/data/csv-file-writer.c @@ -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: diff --git a/src/data/data-in.c b/src/data/data-in.c index 5d4496cf90..db99aa4fb8 100644 --- a/src/data/data-in.c +++ b/src/data/data-in.c @@ -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; } diff --git a/src/data/data-out.c b/src/data/data-out.c index 94e72f668b..d3125955b8 100644 --- a/src/data/data-out.c +++ b/src/data/data-out.c @@ -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; diff --git a/src/data/dictionary.c b/src/data/dictionary.c index 5731d78695..d36f8c519f 100644 --- a/src/data/dictionary.c +++ b/src/data/dictionary.c @@ -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; diff --git a/src/data/file-handle-def.c b/src/data/file-handle-def.c index e4447a080b..121a4909c4 100644 --- a/src/data/file-handle-def.c +++ b/src/data/file-handle-def.c @@ -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; diff --git a/src/data/format.c b/src/data/format.c index 58e16d3075..21162a05ae 100644 --- a/src/data/format.c +++ b/src/data/format.c @@ -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; diff --git a/src/data/gnumeric-reader.c b/src/data/gnumeric-reader.c index b9def31cb6..a1a7415ca1 100644 --- a/src/data/gnumeric-reader.c +++ b/src/data/gnumeric-reader.c @@ -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; } diff --git a/src/data/psql-reader.c b/src/data/psql-reader.c index 4cbd8409ea..630a720711 100644 --- a/src/data/psql-reader.c +++ b/src/data/psql-reader.c @@ -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 diff --git a/src/data/session.c b/src/data/session.c index a308ec1f62..c6f5031a53 100644 --- a/src/data/session.c +++ b/src/data/session.c @@ -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; diff --git a/src/data/sys-file-reader.c b/src/data/sys-file-reader.c index 4a7476ae04..35f40d3dbc 100644 --- a/src/data/sys-file-reader.c +++ b/src/data/sys-file-reader.c @@ -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, diff --git a/src/data/sys-file-writer.c b/src/data/sys-file-writer.c index 5003ca2b89..d02369856f 100644 --- a/src/data/sys-file-writer.c +++ b/src/data/sys-file-writer.c @@ -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'); } diff --git a/src/data/value.c b/src/data/value.c index 144f39b524..ce80076235 100644 --- a/src/data/value.c +++ b/src/data/value.c @@ -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 diff --git a/src/data/value.h b/src/data/value.h index 9205bc1a03..b1b27ed16e 100644 --- a/src/data/value.h +++ b/src/data/value.h @@ -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; diff --git a/src/data/variable.c b/src/data/variable.c index 72d9ade675..fe4645ee41 100644 --- a/src/data/variable.c +++ b/src/data/variable.c @@ -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); } /* 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); diff --git a/src/data/vector.c b/src/data/vector.c index 87046ad42b..7c8ec4178d 100644 --- a/src/data/vector.c +++ b/src/data/vector.c @@ -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); } diff --git a/src/language/control/repeat.c b/src/language/control/repeat.c index 72d45c2c2f..b2c2bb413e 100644 --- a/src/language/control/repeat.c +++ b/src/language/control/repeat.c @@ -30,8 +30,10 @@ #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); } diff --git a/src/language/data-io/combine-files.c b/src/language/data-io/combine-files.c index 21736da8c6..b7ba87ab7c 100644 --- a/src/language/data-io/combine-files.c +++ b/src/language/data-io/combine-files.c @@ -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) diff --git a/src/language/data-io/placement-parser.c b/src/language/data-io/placement-parser.c index 3360e8d739..55e1b5d501 100644 --- a/src/language/data-io/placement-parser.c +++ b/src/language/data-io/placement-parser.c @@ -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); diff --git a/src/language/data-io/placement-parser.h b/src/language/data-io/placement-parser.h index 962b97b4bc..8f8d68c4ce 100644 --- a/src/language/data-io/placement-parser.h +++ b/src/language/data-io/placement-parser.h @@ -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); diff --git a/src/language/data-io/print.c b/src/language/data-io/print.c index cffa3bd49f..e2c5adc073 100644 --- a/src/language/data-io/print.c +++ b/src/language/data-io/print.c @@ -17,6 +17,7 @@ #include #include +#include #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); } -/* 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); } + } +} + +/* 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); + } +} + /* Frees TRNS. */ static bool print_trns_free (void *trns_) diff --git a/src/language/dictionary/modify-variables.c b/src/language/dictionary/modify-variables.c index d73b95e8a2..f0b03d48f9 100644 --- a/src/language/dictionary/modify-variables.c +++ b/src/language/dictionary/modify-variables.c @@ -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 diff --git a/src/language/dictionary/mrsets.c b/src/language/dictionary/mrsets.c index 2f7c8f6d29..9e1ca3b0d0 100644 --- a/src/language/dictionary/mrsets.c +++ b/src/language/dictionary/mrsets.c @@ -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)); diff --git a/src/language/dictionary/vector.c b/src/language/dictionary/vector.c index b0d696154b..512ec3c2bf 100644 --- a/src/language/dictionary/vector.c +++ b/src/language/dictionary/vector.c @@ -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)); diff --git a/src/language/expressions/parse.c b/src/language/expressions/parse.c index 24380e2819..7b845a8d36 100644 --- a/src/language/expressions/parse.c +++ b/src/language/expressions/parse.c @@ -39,6 +39,7 @@ #include "libpspp/pool.h" #include "libpspp/str.h" +#include "gl/c-strcase.h" #include "gl/xalloc.h" /* 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 diff --git a/src/language/lexer/scan.c b/src/language/lexer/scan.c index caf294a9d1..de75eeef3b 100644 --- a/src/language/lexer/scan.c +++ b/src/language/lexer/scan.c @@ -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); diff --git a/src/language/lexer/token.c b/src/language/lexer/token.c index 89a5cf0102..80c6615c5a 100644 --- a/src/language/lexer/token.c +++ b/src/language/lexer/token.c @@ -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) diff --git a/src/language/lexer/variable-parser.c b/src/language/lexer/variable-parser.c index 5c19b81e2f..8c4f8fe8a6 100644 --- a/src/language/lexer/variable-parser.c +++ b/src/language/lexer/variable-parser.c @@ -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; diff --git a/src/language/stats/aggregate.c b/src/language/stats/aggregate.c index ff6c1fb59e..4d95e62e64 100644 --- a/src/language/stats/aggregate.c +++ b/src/language/stats/aggregate.c @@ -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) { diff --git a/src/language/stats/autorecode.c b/src/language/stats/autorecode.c index 95fb8d173c..b2c4d4844c 100644 --- a/src/language/stats/autorecode.c +++ b/src/language/stats/autorecode.c @@ -37,9 +37,10 @@ #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++) diff --git a/src/language/stats/descriptives.c b/src/language/stats/descriptives.c index 54bc49d946..8efb5f1fb4 100644 --- a/src/language/stats/descriptives.c +++ b/src/language/stats/descriptives.c @@ -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]; diff --git a/src/language/stats/npar.c b/src/language/stats/npar.c index 737c94e3d2..fbcdeae058 100644 --- a/src/language/stats/npar.c +++ b/src/language/stats/npar.c @@ -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, diff --git a/src/libpspp/automake.mk b/src/libpspp/automake.mk index 2f81243cf9..77e34860ec 100644 --- a/src/libpspp/automake.mk +++ b/src/libpspp/automake.mk @@ -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 \ diff --git a/src/libpspp/hash-functions.c b/src/libpspp/hash-functions.c index c43017313a..7a8d8162ec 100644 --- a/src/libpspp/hash-functions.c +++ b/src/libpspp/hash-functions.c @@ -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) diff --git a/src/libpspp/hash-functions.h b/src/libpspp/hash-functions.h index 86414fb1c2..f792ba2a96 100644 --- a/src/libpspp/hash-functions.h +++ b/src/libpspp/hash-functions.h @@ -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); diff --git a/src/libpspp/i18n.c b/src/libpspp/i18n.c index 0819299d37..dca85db4f0 100644 --- a/src/libpspp/i18n.c +++ b/src/libpspp/i18n.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #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; } +/* 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); +} + bool get_encoding_info (struct encoding_info *e, const char *name) { diff --git a/src/libpspp/i18n.h b/src/libpspp/i18n.h index 4dbf61a299..6722b5cec9 100644 --- a/src/libpspp/i18n.h +++ b/src/libpspp/i18n.h @@ -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 *); /* Information about character encodings. */ diff --git a/src/libpspp/misc.c b/src/libpspp/misc.c index de1ce3f5d5..c0120df82b 100644 --- a/src/libpspp/misc.c +++ b/src/libpspp/misc.c @@ -16,6 +16,7 @@ #include #include "misc.h" +#include /* 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; +} diff --git a/src/libpspp/misc.h b/src/libpspp/misc.h index c2f865d90c..1698297dc4 100644 --- a/src/libpspp/misc.h +++ b/src/libpspp/misc.h @@ -17,6 +17,7 @@ #if !libpspp_misc_h #define libpspp_misc_h 1 +#include #include #include @@ -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 */ diff --git a/src/libpspp/str.c b/src/libpspp/str.c index 44e4a1da28..d7c71b11f5 100644 --- a/src/libpspp/str.c +++ b/src/libpspp/str.c @@ -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) diff --git a/src/libpspp/str.h b/src/libpspp/str.h index e017696cf6..306bd8ba89 100644 --- a/src/libpspp/str.h +++ b/src/libpspp/str.h @@ -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, diff --git a/src/libpspp/stringi-map.c b/src/libpspp/stringi-map.c index d3e5144de7..7b8d398d03 100644 --- a/src/libpspp/stringi-map.c +++ b/src/libpspp/stringi-map.c @@ -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 #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; diff --git a/src/libpspp/stringi-set.c b/src/libpspp/stringi-set.c index a7ae699471..b442a41567 100644 --- a/src/libpspp/stringi-set.c +++ b/src/libpspp/stringi-set.c @@ -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 index 0000000000..34e650952a --- /dev/null +++ b/src/libpspp/u8-line.c @@ -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 . */ + +#include + +#include "libpspp/u8-line.h" +#include +#include +#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 index 0000000000..2674e2e275 --- /dev/null +++ b/src/libpspp/u8-line.h @@ -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 . */ + +#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 */ diff --git a/src/output/ascii.c b/src/output/ascii.c index 9614e6aef8..756235c5cb 100644 --- a/src/output/ascii.c +++ b/src/output/ascii.c @@ -22,8 +22,8 @@ #include #include #include -#include #include +#include #include #include @@ -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); +} /* 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; diff --git a/src/output/ascii.h b/src/output/ascii.h index 5d324d8464..bf68cafec7 100644 --- a/src/output/ascii.h +++ b/src/output/ascii.h @@ -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 */ diff --git a/src/output/measure.c b/src/output/measure.c index 60894f1cc2..faeef969b0 100644 --- a/src/output/measure.c +++ b/src/output/measure.c @@ -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; } diff --git a/src/ui/gui/aggregate-dialog.c b/src/ui/gui/aggregate-dialog.c index 8b54ed584a..5d477d30c5 100644 --- a/src/ui/gui/aggregate-dialog.c +++ b/src/ui/gui/aggregate-dialog.c @@ -18,6 +18,7 @@ #include "dialog-common.h" +#include #include #include @@ -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); diff --git a/src/ui/gui/chi-square-dialog.c b/src/ui/gui/chi-square-dialog.c index 5d4cc4b431..df408921d1 100644 --- a/src/ui/gui/chi-square-dialog.c +++ b/src/ui/gui/chi-square-dialog.c @@ -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; } diff --git a/src/ui/gui/count-dialog.c b/src/ui/gui/count-dialog.c index 135cc47330..c73032292b 100644 --- a/src/ui/gui/count-dialog.c +++ b/src/ui/gui/count-dialog.c @@ -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; } diff --git a/src/ui/gui/oneway-anova-dialog.c b/src/ui/gui/oneway-anova-dialog.c index 7992b94c71..d656366cf0 100644 --- a/src/ui/gui/oneway-anova-dialog.c +++ b/src/ui/gui/oneway-anova-dialog.c @@ -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; } diff --git a/src/ui/gui/psppire-dialog-action-binomial.c b/src/ui/gui/psppire-dialog-action-binomial.c index 97a5f3cfcc..019332eaba 100644 --- a/src/ui/gui/psppire-dialog-action-binomial.c +++ b/src/ui/gui/psppire-dialog-action-binomial.c @@ -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; } diff --git a/src/ui/gui/psppire-dialog-action-factor.c b/src/ui/gui/psppire-dialog-action-factor.c index 6b6144a6fb..2c6b3e63b3 100644 --- a/src/ui/gui/psppire-dialog-action-factor.c +++ b/src/ui/gui/psppire-dialog-action-factor.c @@ -26,6 +26,7 @@ #include "psppire-dialog.h" #include "builder-wrapper.h" #include "psppire-scanf.h" +#include #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; } diff --git a/src/ui/gui/psppire-dialog-action-frequencies.c b/src/ui/gui/psppire-dialog-action-frequencies.c index b2ddd196a3..6f8049009e 100644 --- a/src/ui/gui/psppire-dialog-action-frequencies.c +++ b/src/ui/gui/psppire-dialog-action-frequencies.c @@ -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; } diff --git a/src/ui/gui/psppire-dialog-action-logistic.c b/src/ui/gui/psppire-dialog-action-logistic.c index 0426931e85..85137868c6 100644 --- a/src/ui/gui/psppire-dialog-action-logistic.c +++ b/src/ui/gui/psppire-dialog-action-logistic.c @@ -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; } diff --git a/src/ui/gui/psppire-output-window.c b/src/ui/gui/psppire-output-window.c index 61f3bbdd28..d1d7e89166 100644 --- a/src/ui/gui/psppire-output-window.c +++ b/src/ui/gui/psppire-output-window.c @@ -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)); } - 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); diff --git a/src/ui/gui/psppire-val-chooser.c b/src/ui/gui/psppire-val-chooser.c index 01d694e27e..eb233b4ae5 100644 --- a/src/ui/gui/psppire-val-chooser.c +++ b/src/ui/gui/psppire-val-chooser.c @@ -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; }; } diff --git a/src/ui/gui/psppire-val-chooser.h b/src/ui/gui/psppire-val-chooser.h index 5c8175bae3..0f487a45db 100644 --- a/src/ui/gui/psppire-val-chooser.h +++ b/src/ui/gui/psppire-val-chooser.h @@ -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); diff --git a/src/ui/gui/psppire-var-view.c b/src/ui/gui/psppire-var-view.c index 5ef5b86dac..c71a5e6610 100644 --- a/src/ui/gui/psppire-var-view.c +++ b/src/ui/gui/psppire-var-view.c @@ -21,6 +21,7 @@ #include "psppire-var-ptr.h" #include "psppire-select-dest.h" +#include #include #include @@ -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; +} + + + diff --git a/src/ui/gui/psppire-var-view.h b/src/ui/gui/psppire-var-view.h index 090966b526..a8fb0565ca 100644 --- a/src/ui/gui/psppire-var-view.h +++ b/src/ui/gui/psppire-var-view.h @@ -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); diff --git a/src/ui/gui/recode-dialog.c b/src/ui/gui/recode-dialog.c index 05984dd44b..f8d21336e1 100644 --- a/src/ui/gui/recode-dialog.c +++ b/src/ui/gui/recode-dialog.c @@ -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; } diff --git a/src/ui/gui/select-cases-dialog.c b/src/ui/gui/select-cases-dialog.c index c03906a6a8..c3bc123c05 100644 --- a/src/ui/gui/select-cases-dialog.c +++ b/src/ui/gui/select-cases-dialog.c @@ -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; } diff --git a/src/ui/gui/t-test-options.c b/src/ui/gui/t-test-options.c index 671eba0c32..467d4321ff 100644 --- a/src/ui/gui/t-test-options.c +++ b/src/ui/gui/t-test-options.c @@ -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); } diff --git a/src/ui/syntax-gen.c b/src/ui/syntax-gen.c index bcec18e556..dca3bd3c5b 100644 --- a/src/ui/syntax-gen.c +++ b/src/ui/syntax-gen.c @@ -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; } diff --git a/tests/automake.mk b/tests/automake.mk index 6c4cef20aa..737df306f8 100644 --- a/tests/automake.mk +++ b/tests/automake.mk @@ -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 index 0000000000..ef62c664f8 --- /dev/null +++ b/tests/data/dictionary.at @@ -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 diff --git a/tests/data/sys-file-reader.at b/tests/data/sys-file-reader.at index 9fd8c09e19..c5699b136a 100644 --- a/tests/data/sys-file-reader.at +++ b/tests/data/sys-file-reader.at @@ -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ÀÈÌÑÒ,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ÀÈÌÑÒ,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Éq256,Format: A256,,1 +séq256,Format: A256,,1 str600,Format: A600,,2 Table: Data List -sÉq256,str600 +séq256,str600 abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@a,abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@#abcdefghijklmnopqrstuvwxyz ]) done diff --git a/tests/language/data-io/update.at b/tests/language/data-io/update.at index 0a2ea2896f..41ccf21043 100644 --- a/tests/language/data-io/update.at +++ b/tests/language/data-io/update.at @@ -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 diff --git a/tests/language/stats/npar.at b/tests/language/stats/npar.at index f5eb2a404c..2e5d886f3f 100644 --- a/tests/language/stats/npar.at +++ b/tests/language/stats/npar.at @@ -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 diff --git a/tests/libpspp/stringi-map-test.c b/tests/libpspp/stringi-map-test.c index a8dd9dd6b7..3ae027fb36 100644 --- a/tests/libpspp/stringi-map-test.c +++ b/tests/libpspp/stringi-map-test.c @@ -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")); diff --git a/tests/libpspp/stringi-set-test.c b/tests/libpspp/stringi-set-test.c index a20e84ce18..daf7b2fa6d 100644 --- a/tests/libpspp/stringi-set-test.c +++ b/tests/libpspp/stringi-set-test.c @@ -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 #include "libpspp/compiler.h" +#include "libpspp/i18n.h" #include "libpspp/str.h" /* 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); diff --git a/tests/output/ascii.at b/tests/output/ascii.at index 07b440efb5..a3f66b933b 100644 --- a/tests/output/ascii.at +++ b/tests/output/ascii.at @@ -520,6 +520,47 @@ _à_e_î_o̧_ũ_y ]) 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 diff --git a/tests/output/render-test.c b/tests/output/render-test.c index 0376e5b49b..5f4c1da00b 100644 --- a/tests/output/render-test.c +++ b/tests/output/render-test.c @@ -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); } }