data-in: Eliminate "implied_decimals" parameter from data_in().
[pspp] / src / ui / syntax-gen.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008, 2010 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include <ui/syntax-gen.h>
20
21 #include <ctype.h>
22 #include <mbchar.h>
23
24 #include "data/data-in.h"
25 #include "data/data-out.h"
26 #include "data/format.h"
27 #include "data/value.h"
28 #include "libpspp/assertion.h"
29 #include "libpspp/cast.h"
30 #include "libpspp/message.h"
31 #include "libpspp/str.h"
32
33 /* Appends to OUTPUT a pair of hex digits for each byte in IN. */
34 static void
35 syntax_gen_hex_digits (struct string *output, struct substring in)
36 {
37   size_t i;
38   for (i = 0; i < in.length; i++)
39     {
40       unsigned char c = in.string[i];
41       ds_put_char (output, "0123456789ABCDEF"[c >> 4]);
42       ds_put_char (output, "0123456789ABCDEF"[c & 0xf]);
43     }
44 }
45
46 /* Returns true if IN contains any control characters, false
47    otherwise */
48 static bool
49 has_control_chars (struct substring in)
50 {
51   size_t i;
52
53   for (i = 0; i < in.length; i++)
54     if (iscntrl ((unsigned char) in.string[i]))
55       return true;
56   return false;
57 }
58
59 static bool
60 has_single_quote (struct substring str)
61 {
62   return (SIZE_MAX != ss_find_char (str, '\''));
63 }
64
65 static bool
66 has_double_quote (struct substring str)
67 {
68   return (SIZE_MAX != ss_find_char (str, '"'));
69 }
70
71 /* Appends to OUTPUT valid PSPP syntax for a quoted string that
72    contains IN.
73
74    IN must be encoded in UTF-8, and the quoted result will also
75    be encoded in UTF-8.
76
77    The string will be output as a regular quoted string unless it
78    contains control characters, in which case it is output as a
79    hex string. */
80 void
81 syntax_gen_string (struct string *output, struct substring in)
82 {
83   if (has_control_chars (in))
84     {
85       ds_put_cstr (output, "X'");
86       syntax_gen_hex_digits (output, in);
87       ds_put_char (output, '\'');
88     }
89   else
90     {
91       int quote;
92       size_t i;
93
94       /* This seemingly simple implementation is possible, because UTF-8
95          guarantees that bytes corresponding to basic characters (such as
96          '\'') cannot appear in a multi-byte character sequence except to
97          represent that basic character.
98       */
99       assert (is_basic ('\''));
100
101       quote = has_double_quote (in) && !has_single_quote (in) ? '\'' : '"';
102       ds_put_char (output, quote);
103       for (i = 0; i < in.length; i++)
104         {
105           char c = in.string[i];
106           if (c == quote)
107             ds_put_char (output, quote);
108           ds_put_char (output, c);
109         }
110       ds_put_char (output, quote);
111     }
112 }
113
114 /* Appends to OUTPUT a representation of NUMBER in PSPP syntax.
115    The representation is precise, that is, when PSPP parses the
116    representation, its value will be exactly NUMBER.  (This might
117    not be the case on a C implementation where double has a
118    different representation.)
119
120    If NUMBER is the system-missing value, it is output as the
121    identifier SYSMIS.  This may not be appropriate, because
122    SYSMIS is not consistently parsed throughout PSPP syntax as
123    the system-missing value.  But in such circumstances the
124    system-missing value would not be meaningful anyhow, so the
125    caller should refrain from supplying the system-missing value
126    in such cases.
127
128    A value of LOWEST or HIGHEST is not treated specially.
129
130    If FORMAT is null, then the representation will be in numeric
131    form, e.g. 123 or 1.23e10.
132
133    If FORMAT is non-null, then it must point to a numeric format.
134    If the format is one easier for a user to understand when
135    expressed as a string than as a number (for example, a date
136    format), and the string representation precisely represents
137    NUMBER, then the string representation is written to OUTPUT.
138    Otherwise, NUMBER is output as if FORMAT was a null
139    pointer. */
140 void
141 syntax_gen_number (struct string *output,
142                    double number, const struct fmt_spec *format)
143 {
144   assert (format == NULL || fmt_is_numeric (format->type));
145   if (format != NULL
146       && (format->type
147           & (FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT)))
148     {
149       union value v_in, v_out;
150       char *s;
151       bool ok;
152
153       v_in.f = number;
154       s = data_out (&v_in, "FIXME",  format);
155       msg_disable ();
156       /* FIXME: UTF8 encoded strings will fail here */
157       ok = data_in (ss_cstr (s), LEGACY_NATIVE,
158                     format->type, 0, 0, NULL, &v_out, 0);
159       msg_enable ();
160       if (ok && v_out.f == number)
161         {
162           syntax_gen_string (output, ss_cstr (s));
163           free (s);
164           return;
165         }
166       free (s);
167     }
168
169   if (number == SYSMIS)
170     ds_put_cstr (output, "SYSMIS");
171   else
172     {
173       /* FIXME: This should consistently yield precisely the same
174          value as NUMBER on input, but its results for values
175          cannot be exactly represented in decimal are ugly: many
176          of them will have far more decimal digits than are
177          needed.  The free-format floating point output routine
178          from Steele and White, "How to Print Floating-Point
179          Numbers Accurately" is really what we want.  The MPFR
180          library has an implementation of this, or equivalent
181          functionality, in its mpfr_strtofr routine, but it would
182          not be nice to make PSPP depend on this.  Probably, we
183          should implement something equivalent to it. */
184       ds_put_format (output, "%.*g", DBL_DIG + 1, number);
185     }
186 }
187
188 /* Appends to OUTPUT a representation of VALUE, which has the
189    specified WIDTH.  If FORMAT is non-null, it influences the
190    output format.  The representation is precise, that is, when
191    PSPP parses the representation, its value will be exactly
192    VALUE. */
193 void
194 syntax_gen_value (struct string *output, const union value *value, int width,
195                   const struct fmt_spec *format)
196 {
197   assert (format == NULL || fmt_var_width (format) == width);
198   if (width == 0)
199     syntax_gen_number (output, value->f, format);
200   else
201     {
202       char *s = CHAR_CAST_BUG (char *, value_str (value, width));
203       syntax_gen_string (output, ss_buffer (s, width));
204     }
205 }
206
207 /* Appends <low> THRU <high> to OUTPUT.  If LOW is LOWEST, then
208    it is formatted as the identifier LO; if HIGH is HIGHEST, then
209    it is formatted as the identifier HI.  Otherwise, LOW and HIGH
210    are formatted as with a call to syntax_gen_num with the specified
211    FORMAT.
212
213    This is the opposite of the function parse_num_range. */
214 void
215 syntax_gen_num_range (struct string *output, double low, double high,
216                       const struct fmt_spec *format)
217 {
218   if (low == LOWEST)
219     ds_put_cstr (output, "LO");
220   else
221     syntax_gen_number (output, low, format);
222
223   ds_put_cstr (output, " THRU ");
224
225   if (high == HIGHEST)
226     ds_put_cstr (output, "HI");
227   else
228     syntax_gen_number (output, high, format);
229 }
230
231 /* Same as syntax_gen_pspp, below, but takes a va_list. */
232 void
233 syntax_gen_pspp_valist (struct string *output, const char *format,
234                         va_list args)
235 {
236   for (;;)
237     {
238       size_t copy = strcspn (format, "%");
239       ds_put_substring (output, ss_buffer (format, copy));
240       format += copy;
241
242       if (*format == '\0')
243         return;
244       assert (*format == '%');
245       format++;
246       switch (*format++)
247         {
248         case 's':
249           {
250             const char *s = va_arg (args, char *);
251             switch (*format++)
252               {
253               case 'q':
254                 syntax_gen_string (output, ss_cstr (s));
255                 break;
256               case 's':
257                 ds_put_cstr (output, s);
258                 break;
259               default:
260                 NOT_REACHED ();
261               }
262           }
263           break;
264
265         case 'd':
266           {
267             int i = va_arg (args, int);
268             ds_put_format (output, "%d", i);
269           }
270           break;
271
272         case 'f':
273           {
274             double d = va_arg (args, double);
275             switch (*format++)
276               {
277               case 'p':
278                 ds_put_format (output, "%f", d);
279                 break;
280               default:
281                 NOT_REACHED ();
282               }
283             break;
284           }
285
286         case '%':
287           ds_put_char (output, '%');
288           break;
289
290         default:
291           NOT_REACHED ();
292         }
293     }
294 }
295
296 /* printf-like function specialized for outputting PSPP syntax.
297    FORMAT is appended to OUTPUT.  The following substitutions are
298    supported:
299
300      %sq: The char * argument is formatted as a PSPP string, as
301           if with a call to syntax_gen_string.
302
303      %ss: The char * argument is appended literally.
304
305      %d: Same as printf's %d.
306
307      %fp: The double argument is formatted precisely as a PSPP
308           number, as if with a call to syntax_gen_number with a
309           null FORMAT argument.
310
311      %%: Literal %.
312
313    (These substitutions were chosen to allow GCC to check for
314    correct argument types.)
315
316    This function is somewhat experimental.  If it proves useful,
317    the allowed substitutions will almost certainly be
318    expanded. */
319 void
320 syntax_gen_pspp (struct string *output, const char *format, ...)
321 {
322   va_list args;
323   va_start (args, format);
324   syntax_gen_pspp_valist (output, format, args);
325   va_end (args);
326 }