/* PSPPIRE - a graphical user interface for PSPP. Copyright (C) 2008, 2010, 2011, 2014 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 "ui/syntax-gen.h" #include #include #include "data/data-in.h" #include "data/data-out.h" #include "data/format.h" #include "data/value.h" #include "libpspp/assertion.h" #include "libpspp/cast.h" #include "libpspp/i18n.h" #include "libpspp/message.h" #include "data/settings.h" #include "libpspp/str.h" #include "libpspp/misc.h" #include "gl/c-ctype.h" #include "gl/ftoastr.h" /* 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_byte (output, "0123456789ABCDEF"[c>> 4]); ds_put_byte (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_byte (str, '\'')); } static bool has_double_quote (struct substring str) { return (SIZE_MAX != ss_find_byte (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_byte (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_byte (output, quote); for (i = 0; i < in.length; i++) { char c = in.string[i]; if (c == quote) ds_put_byte (output, quote); ds_put_byte (output, c); } ds_put_byte (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 && (fmt_get_category (format->type) & (FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT))) { union value v_in, v_out; char *s, *error; bool ok; v_in.f = number; s = data_out (&v_in, "FIXME", *format, settings_get_fmt_settings ()); /* FIXME: UTF8 encoded strings will fail here */ error = data_in (ss_cstr (s), C_ENCODING, format->type, settings_get_fmt_settings (), &v_out, 0, NULL); ok = error == NULL; free (error); if (ok && v_out.f == number) { syntax_gen_string (output, ss_cstr (s)); free (s); return; } free (s); } if (number == SYSMIS) ds_put_cstr (output, "SYSMIS"); else { char s[DBL_BUFSIZE_BOUND]; c_dtoastr (s, sizeof s, 0, 0, number); ds_put_cstr (output, s); } } /* 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 (CHAR_CAST (const char *, 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 (;;) { char qualifier[16]; int precision = -1; char directive; size_t copy = strcspn (format, "%"); ds_put_substring (output, ss_buffer (format, copy)); format += copy; if (*format == '0円') return; assert (*format == '%'); format++; directive = *format++; if (directive == '.') { int x = 0; while (directive = *format++, c_isdigit (directive)) { assert (x < 16); qualifier[x++] = directive; } qualifier[x++] = '0円'; precision = atoi (qualifier); } switch (directive) { 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': case 'g': { char conv[32]; double d = va_arg (args, double); int x = 0; conv[x++] = '%'; conv[x] = '0円'; if (precision != -1) { strcat (conv, "."); strcat (conv, qualifier); x += strlen (qualifier) + 1; } conv[x++] = directive; conv[x++] = '0円'; ds_put_c_format (output, conv, d); break; } case '%': ds_put_byte (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. %f %g: Same as printf. %%: 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); }

AltStyle によって変換されたページ (->オリジナル) /