From 57e1bd2b5e86c0f47ed8fcc23c4945034afb2053 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 5 Mar 2008 06:30:31 +0000 Subject: [PATCH] Patch #6427. Reviewed by John Darrington. * comments-dialog.c: Adapt to new syntax generating code in ui/syntax-gen.[ch]. * data-editor.c: Ditto. * recode-dialog.c: Ditto. * t-test-independent-samples-dialog.c: Ditto. * automake.mk: Add new files. * syntax-gen.c: New file. Based on former src/libpspp/syntax-gen.c but rewritten. * syntax-gen.h: New file. Based on former src/libpspp/syntax-gen.h but rewritten. * automake.mk: Remove moved files. * syntax-gen.c: Moved to src/ui (and rewritten). * syntax-gen.h: Ditto. --- src/libpspp/ChangeLog | 10 + src/libpspp/automake.mk | 2 - src/libpspp/syntax-gen.c | 100 ------ src/libpspp/syntax-gen.h | 24 -- src/ui/ChangeLog | 12 + src/ui/automake.mk | 4 +- src/ui/gui/ChangeLog | 13 + src/ui/gui/comments-dialog.c | 34 +- src/ui/gui/data-editor.c | 11 +- src/ui/gui/recode-dialog.c | 16 +- .../gui/t-test-independent-samples-dialog.c | 12 +- src/ui/syntax-gen.c | 319 ++++++++++++++++++ src/ui/syntax-gen.h | 45 +++ 13 files changed, 436 insertions(+), 166 deletions(-) delete mode 100644 src/libpspp/syntax-gen.c delete mode 100644 src/libpspp/syntax-gen.h create mode 100644 src/ui/syntax-gen.c create mode 100644 src/ui/syntax-gen.h diff --git a/src/libpspp/ChangeLog b/src/libpspp/ChangeLog index c201dc07..fcd88f1e 100644 --- a/src/libpspp/ChangeLog +++ b/src/libpspp/ChangeLog @@ -1,3 +1,13 @@ +2008-03-04 Ben Pfaff + + Patch #6427. Reviewed by John Darrington. + + * automake.mk: Remove moved files. + + * syntax-gen.c: Moved to src/ui (and rewritten). + + * syntax-gen.h: Ditto. + 2008-03-04 Ben Pfaff Patch #6441. Reviewed by John Darrington. diff --git a/src/libpspp/automake.mk b/src/libpspp/automake.mk index 4c714f20..a697e402 100644 --- a/src/libpspp/automake.mk +++ b/src/libpspp/automake.mk @@ -57,8 +57,6 @@ src_libpspp_libpspp_a_SOURCES = \ src/libpspp/start-date.h \ src/libpspp/str.c \ src/libpspp/str.h \ - src/libpspp/syntax-gen.c \ - src/libpspp/syntax-gen.h \ src/libpspp/taint.c \ src/libpspp/taint.h \ src/libpspp/tower.c \ diff --git a/src/libpspp/syntax-gen.c b/src/libpspp/syntax-gen.c deleted file mode 100644 index a1cfaf5f..00000000 --- a/src/libpspp/syntax-gen.c +++ /dev/null @@ -1,100 +0,0 @@ -/* PSPP - a program for statistical analysis. - Copyright (C) 2007 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 -#include - -#include -#include -#include -#include - -#include "syntax-gen.h" - - -static bool -has_single_quote (const struct string *str) -{ - return (SIZE_MAX != ds_find_char (str, '\'')); -} - -static bool -has_double_quote (const struct string *str) -{ - return (SIZE_MAX != ds_find_char (str, '"')); -} - -/* - Quotes the string STR. If STR contains no '\'' character, then - the returned value will be enclosed in single quotes. Else, if STR - contains no '"' character, then it will be enclosed in double - quotes. Otherwise, it will be enclosed in single quotes, and each - '\'' will be padded with another '\''. - - STR must be encoded in UTF-8, and the quoted result will also be - encoded in UTF-8. -*/ -void -gen_quoted_string (struct string *str) -{ - char c; - static const char single_quote[] = "'"; - static const char double_quote[] = "\""; - - struct string quoted_str; - - bool pad_single_quotes = false; - const char *delimiter ; - char *s = ds_cstr (str); - - if ( has_double_quote (str)) - { - delimiter = single_quote; - if ( has_single_quote (str)) - pad_single_quotes = true; - } - else - { - delimiter = double_quote; - } - - /* This seemingly simple implementation is possible, because UTF-8 - guarantees that bytes corresponding to basic characters (such as - '\'') cannot appear in a multi-byte character sequence except to - represent that basic character. - */ - assert (is_basic ('\'')); - - /* Initialise with the opening delimiter */ - ds_init_cstr ("ed_str, delimiter); - while ((c = *s++)) - { - ds_put_char ("ed_str, c); - - /* If c is a single quote, then append another one */ - if ( c == '\'' && pad_single_quotes) - ds_put_char ("ed_str, c); - } - - /* Add the closing delimiter */ - ds_put_cstr ("ed_str, delimiter); - - /* Copy the quoted string into str */ - ds_swap (str, "ed_str); - ds_destroy ("ed_str); -} - diff --git a/src/libpspp/syntax-gen.h b/src/libpspp/syntax-gen.h deleted file mode 100644 index d8bcb4e1..00000000 --- a/src/libpspp/syntax-gen.h +++ /dev/null @@ -1,24 +0,0 @@ -/* PSPP - a program for statistical analysis. - Copyright (C) 2007 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 SYNTAX_GEN_H -#define SYNTAX_GEN_H - -struct string; - -void gen_quoted_string (struct string *); - -#endif diff --git a/src/ui/ChangeLog b/src/ui/ChangeLog index e17fe343..bc9c192f 100644 --- a/src/ui/ChangeLog +++ b/src/ui/ChangeLog @@ -1,3 +1,15 @@ +2008-03-04 Ben Pfaff + + Patch #6427. Reviewed by John Darrington. + + * automake.mk: Add new files. + + * syntax-gen.c: New file. Based on former + src/libpspp/syntax-gen.c but rewritten. + + * syntax-gen.h: New file. Based on former + src/libpspp/syntax-gen.h but rewritten. + 2007-10-10 Ben Pfaff * src/ui/debugger.c: Use debugger if HAVE_FORK is defined, not diff --git a/src/ui/automake.mk b/src/ui/automake.mk index e1915b28..18b69148 100644 --- a/src/ui/automake.mk +++ b/src/ui/automake.mk @@ -10,4 +10,6 @@ noinst_LIBRARIES += src/ui/libuicommon.a src_ui_libuicommon_a_SOURCES = \ src/ui/debugger.c \ - src/ui/debugger.h + src/ui/debugger.h \ + src/ui/syntax-gen.c \ + src/ui/syntax-gen.h diff --git a/src/ui/gui/ChangeLog b/src/ui/gui/ChangeLog index 580971cb..6e16fdae 100644 --- a/src/ui/gui/ChangeLog +++ b/src/ui/gui/ChangeLog @@ -1,3 +1,16 @@ +2008-03-04 Ben Pfaff + + Patch #6427. Reviewed by John Darrington. + + * comments-dialog.c: Adapt to new syntax generating code in + ui/syntax-gen.[ch]. + + * data-editor.c: Ditto. + + * recode-dialog.c: Ditto. + + * t-test-independent-samples-dialog.c: Ditto. + 2008-02-29 John Darrington * psppire-data-editor.c psppire-data-editor.h: New files. Contains functionality diff --git a/src/ui/gui/comments-dialog.c b/src/ui/gui/comments-dialog.c index b584c0b5..f1a4936e 100644 --- a/src/ui/gui/comments-dialog.c +++ b/src/ui/gui/comments-dialog.c @@ -22,7 +22,7 @@ #include #include "syntax-editor.h" #include "psppire-var-store.h" -#include +#include #include "comments-dialog.h" @@ -244,27 +244,27 @@ generate_syntax (const struct comment_dialog *cd) for (i = 0 ; i < gtk_text_buffer_get_line_count (buffer) ; ++i ) { - struct string line; - GtkTextIter start, end; - gtk_text_buffer_get_iter_at_line (buffer, &start, i); - - end = start; + struct string tmp; + GtkTextIter start; + char *line; - gtk_text_iter_forward_to_line_end (&end); - - if ( gtk_text_iter_ends_line (&start)) - ds_init_cstr (&line, ""); + gtk_text_buffer_get_iter_at_line (buffer, &start, i); + if (gtk_text_iter_ends_line (&start)) + line = g_strdup (""); else - ds_init_cstr (&line, - gtk_text_buffer_get_text (buffer, - &start, &end, - FALSE)); + { + GtkTextIter end = start; + gtk_text_iter_forward_to_line_end (&end); + line = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); + } - gen_quoted_string (&line); + ds_init_empty (&tmp); + syntax_gen_string (&tmp, ss_cstr (line)); + g_free (line); - g_string_append_printf (str, " %s\n", ds_cstr (&line)); + g_string_append_printf (str, " %s\n", ds_cstr (&tmp)); - ds_destroy (&line); + ds_destroy (&tmp); } g_string_append (str, " .\n"); diff --git a/src/ui/gui/data-editor.c b/src/ui/gui/data-editor.c index 0af3c8ff..8afdfa0f 100644 --- a/src/ui/gui/data-editor.c +++ b/src/ui/gui/data-editor.c @@ -61,7 +61,7 @@ #include "syntax-editor.h" #include #include -#include +#include #include "window-manager.h" #include "psppire-data-store.h" @@ -1262,8 +1262,8 @@ save_file (struct data_editor *de) g_assert (de->file_name); - ds_init_cstr (&file_name, de->file_name); - gen_quoted_string (&file_name); + ds_init_empty (&file_name); + syntax_gen_string (&file_name, ss_cstr (de->file_name)); if ( de->save_as_portable ) { @@ -1398,9 +1398,8 @@ open_data_file (const gchar *file_name, struct data_editor *de) struct getl_interface *sss; struct string filename; - ds_init_cstr (&filename, file_name); - - gen_quoted_string (&filename); + ds_init_empty (&filename); + syntax_gen_string (&filename, ss_cstr (file_name)); sss = create_syntax_string_source ("GET FILE=%s.", ds_cstr (&filename)); diff --git a/src/ui/gui/recode-dialog.c b/src/ui/gui/recode-dialog.c index 041566cf..31436746 100644 --- a/src/ui/gui/recode-dialog.c +++ b/src/ui/gui/recode-dialog.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include "psppire-acr.h" @@ -1338,9 +1338,8 @@ new_value_append_syntax (GString *str, const struct new_value *nv) break; case NV_STRING: { - struct string ds; - ds_init_cstr (&ds, nv->v.s); - gen_quoted_string (&ds); + 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); } @@ -1371,9 +1370,8 @@ old_value_append_syntax (GString *str, const struct old_value *ov) break; case OV_STRING: { - struct string ds; - ds_init_cstr (&ds, ov->v.s); - gen_quoted_string (&ds); + struct string ds = DS_EMPTY_INITIALIZER; + syntax_gen_string (&ds, ss_cstr (ov->v.s)); g_string_append (str, ds_cstr (&ds)); ds_destroy (&ds); } @@ -1537,9 +1535,9 @@ generate_syntax (const struct recode_dialog *rd) continue; } - ds_init_cstr (&ls, label); + ds_init_empty (&ls); + syntax_gen_string (&ls, ss_cstr (label)); g_free (label); - gen_quoted_string (&ls); g_string_append_printf (str, "\nVARIABLE LABELS %s %s.", name, ds_cstr (&ls)); diff --git a/src/ui/gui/t-test-independent-samples-dialog.c b/src/ui/gui/t-test-independent-samples-dialog.c index d5f9eaa6..9c3ea41d 100644 --- a/src/ui/gui/t-test-independent-samples-dialog.c +++ b/src/ui/gui/t-test-independent-samples-dialog.c @@ -29,7 +29,7 @@ #include "dict-display.h" #include "widget-io.h" #include "t-test-options.h" -#include +#include #include #include "syntax-editor.h" @@ -177,9 +177,8 @@ generate_syntax (const struct tt_indep_samples_dialog *d) if ( var_is_alpha (group_variable)) { - struct string s; - ds_init_cstr (&s, d->grps->val[0]); - gen_quoted_string (&s); + struct string s = DS_EMPTY_INITIALIZER; + syntax_gen_string (&s, ss_cstr (d->grps->val[0])); g_string_append (str, ds_cstr (&s)); ds_destroy (&s); } @@ -194,9 +193,8 @@ generate_syntax (const struct tt_indep_samples_dialog *d) if ( var_is_alpha (group_variable)) { - struct string s; - ds_init_cstr (&s, d->grps->val[1]); - gen_quoted_string (&s); + struct string s = DS_EMPTY_INITIALIZER; + syntax_gen_string (&s, ss_cstr (d->grps->val[1])); g_string_append (str, ds_cstr (&s)); ds_destroy (&s); } diff --git a/src/ui/syntax-gen.c b/src/ui/syntax-gen.c new file mode 100644 index 00000000..84415c3e --- /dev/null +++ b/src/ui/syntax-gen.c @@ -0,0 +1,319 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2008 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 + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +/* Appends to OUTPUT a pair of hex digits for each byte in IN. */ +static void +syntax_gen_hex_digits (struct string *output, struct substring in) +{ + size_t i; + for (i = 0; i < in.length; i++) + { + unsigned char c = in.string[i]; + ds_put_char (output, "0123456789ABCDEF"[c >> 4]); + ds_put_char (output, "0123456789ABCDEF"[c & 0xf]); + } +} + +/* Returns true if IN contains any control characters, false + otherwise */ +static bool +has_control_chars (struct substring in) +{ + size_t i; + + for (i = 0; i < in.length; i++) + if (iscntrl ((unsigned char) in.string[i])) + return true; + return false; +} + +static bool +has_single_quote (struct substring str) +{ + return (SIZE_MAX != ss_find_char (str, '\'')); +} + +static bool +has_double_quote (struct substring str) +{ + return (SIZE_MAX != ss_find_char (str, '"')); +} + +/* Appends to OUTPUT valid PSPP syntax for a quoted string that + contains IN. + + IN must be encoded in UTF-8, and the quoted result will also + be encoded in UTF-8. + + The string will be output as a regular quoted string unless it + contains control characters, in which case it is output as a + hex string. */ +void +syntax_gen_string (struct string *output, struct substring in) +{ + if (has_control_chars (in)) + { + ds_put_cstr (output, "X'"); + syntax_gen_hex_digits (output, in); + ds_put_char (output, '\''); + } + else + { + int quote; + size_t i; + + /* This seemingly simple implementation is possible, because UTF-8 + guarantees that bytes corresponding to basic characters (such as + '\'') cannot appear in a multi-byte character sequence except to + represent that basic character. + */ + assert (is_basic ('\'')); + + quote = has_double_quote (in) && !has_single_quote (in) ? '\'' : '"'; + ds_put_char (output, quote); + for (i = 0; i < in.length; i++) + { + char c = in.string[i]; + if (c == quote) + ds_put_char (output, quote); + ds_put_char (output, c); + } + ds_put_char (output, quote); + } +} + +/* Appends to OUTPUT a representation of NUMBER in PSPP syntax. + The representation is precise, that is, when PSPP parses the + representation, its value will be exactly NUMBER. (This might + not be the case on a C implementation where double has a + different representation.) + + If NUMBER is the system-missing value, it is output as the + identifier SYSMIS. This may not be appropriate, because + SYSMIS is not consistently parsed throughout PSPP syntax as + the system-missing value. But in such circumstances the + system-missing value would not be meaningful anyhow, so the + caller should refrain from supplying the system-missing value + in such cases. + + A value of LOWEST or HIGHEST is not treated specially. + + If FORMAT is null, then the representation will be in numeric + form, e.g. 123 or 1.23e10. + + If FORMAT is non-null, then it must point to a numeric format. + If the format is one easier for a user to understand when + expressed as a string than as a number (for example, a date + format), and the string representation precisely represents + NUMBER, then the string representation is written to OUTPUT. + Otherwise, NUMBER is output as if FORMAT was a null + pointer. */ +void +syntax_gen_number (struct string *output, + double number, const struct fmt_spec *format) +{ + assert (format == NULL || fmt_is_numeric (format->type)); + if (format != NULL + && (format->type + & (FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT))) + { + union value v_in, v_out; + char buffer[FMT_MAX_NUMERIC_WIDTH]; + bool ok; + + v_in.f = number; + data_out (&v_in, format, buffer); + msg_disable (); + ok = data_in (ss_buffer (buffer, format->w), LEGACY_NATIVE, + format->type, false, 0, 0, &v_out, 0); + msg_enable (); + if (ok && v_out.f == number) + { + syntax_gen_string (output, ss_buffer (buffer, format->w)); + return; + } + } + + if (number == SYSMIS) + ds_put_cstr (output, "SYSMIS"); + else + { + /* FIXME: This should consistently yield precisely the same + value as NUMBER on input, but its results for values + cannot be exactly represented in decimal are ugly: many + of them will have far more decimal digits than are + needed. The free-format floating point output routine + from Steele and White, "How to Print Floating-Point + Numbers Accurately" is really what we want. The MPFR + library has an implementation of this, or equivalent + functionality, in its mpfr_strtofr routine, but it would + not be nice to make PSPP depend on this. Probably, we + should implement something equivalent to it. */ + ds_put_format (output, "%.*g", DBL_DIG + 1, number); + } +} + +/* Appends to OUTPUT a representation of VALUE, which has the + specified WIDTH. If FORMAT is non-null, it influences the + output format. The representation is precise, that is, when + PSPP parses the representation, its value will be exactly + VALUE. */ +void +syntax_gen_value (struct string *output, const union value *value, int width, + const struct fmt_spec *format) +{ + assert (format == NULL || fmt_var_width (format) == width); + if (width == 0) + syntax_gen_number (output, value->f, format); + else + syntax_gen_string (output, ss_buffer (value->s, width)); +} + +/* Appends THRU to OUTPUT. If LOW is LOWEST, then + it is formatted as the identifier LO; if HIGH is HIGHEST, then + it is formatted as the identifier HI. Otherwise, LOW and HIGH + are formatted as with a call to syntax_gen_num with the specified + FORMAT. + + This is the opposite of the function parse_num_range. */ +void +syntax_gen_num_range (struct string *output, double low, double high, + const struct fmt_spec *format) +{ + if (low == LOWEST) + ds_put_cstr (output, "LO"); + else + syntax_gen_number (output, low, format); + + ds_put_cstr (output, " THRU "); + + if (high == HIGHEST) + ds_put_cstr (output, "HI"); + else + syntax_gen_number (output, high, format); +} + +/* Same as syntax_gen_pspp, below, but takes a va_list. */ +void +syntax_gen_pspp_valist (struct string *output, const char *format, + va_list args) +{ + for (;;) + { + size_t copy = strcspn (format, "%"); + ds_put_substring (output, ss_buffer (format, copy)); + format += copy; + + if (*format == '\0') + return; + assert (*format == '%'); + format++; + switch (*format++) + { + case 's': + { + const char *s = va_arg (args, char *); + switch (*format++) + { + case 'q': + syntax_gen_string (output, ss_cstr (s)); + break; + case 's': + ds_put_cstr (output, s); + break; + default: + NOT_REACHED (); + } + } + break; + + case 'd': + { + int i = va_arg (args, int); + ds_put_format (output, "%d", i); + } + break; + + case 'f': + { + double d = va_arg (args, double); + switch (*format++) + { + case 'p': + ds_put_format (output, "%f", d); + break; + default: + NOT_REACHED (); + } + break; + } + + case '%': + ds_put_char (output, '%'); + break; + + default: + NOT_REACHED (); + } + } +} + +/* printf-like function specialized for outputting PSPP syntax. + FORMAT is appended to OUTPUT. The following substitutions are + supported: + + %sq: The char * argument is formatted as a PSPP string, as + if with a call to syntax_gen_string. + + %ss: The char * argument is appended literally. + + %d: Same as printf's %d. + + %fp: The double argument is formatted precisely as a PSPP + number, as if with a call to syntax_gen_number with a + null FORMAT argument. + + %%: Literal %. + + (These substitutions were chosen to allow GCC to check for + correct argument types.) + + This function is somewhat experimental. If it proves useful, + the allowed substitutions will almost certainly be + expanded. */ +void +syntax_gen_pspp (struct string *output, const char *format, ...) +{ + va_list args; + va_start (args, format); + syntax_gen_pspp_valist (output, format, args); + va_end (args); +} diff --git a/src/ui/syntax-gen.h b/src/ui/syntax-gen.h new file mode 100644 index 00000000..501f1f37 --- /dev/null +++ b/src/ui/syntax-gen.h @@ -0,0 +1,45 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2007, 2008 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 SYNTAX_GEN_H +#define SYNTAX_GEN_H 1 + +/* These functions aid in composing PSPP syntax. */ + +#include +#include +#include + +struct fmt_spec; +struct substring; +struct string; +union value; + +void syntax_gen_string (struct string *output, struct substring in); +void syntax_gen_number (struct string *output, + double, const struct fmt_spec *format); +void syntax_gen_value (struct string *output, const union value *value, + int width, const struct fmt_spec *format); +void syntax_gen_num_range (struct string *output, double low, double high, + const struct fmt_spec *format); + +void syntax_gen_pspp_valist (struct string *output, const char *format, + va_list) + PRINTF_FORMAT (2, 0); +void syntax_gen_pspp (struct string *output, const char *format, ...) + PRINTF_FORMAT (2, 3); + +#endif /* format-syntax.h */ -- 2.30.2