35de44da0dc9dfe71e1ba9cb3ae2a19883b47192
[pspp-builds.git] / 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_byte (output, "0123456789ABCDEF"[c >> 4]);
42       ds_put_byte (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_byte (str, '\''));
63 }
64
65 static bool
66 has_double_quote (struct substring str)
67 {
68   return (SIZE_MAX != ss_find_byte (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_byte (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_byte (output, quote);
103       for (i = 0; i < in.length; i++)
104         {
105           char c = in.string[i];
106           if (c == quote)
107             ds_put_byte (output, quote);
108           ds_put_byte (output, c);
109         }
110       ds_put_byte (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, *error;
151       bool ok;
152
153       v_in.f = number;
154       s = data_out (&v_in, "FIXME",  format);
155
156       /* FIXME: UTF8 encoded strings will fail here */
157       error = data_in (ss_cstr (s), LEGACY_NATIVE,
158                        format->type, &v_out, 0, NULL);
159       ok = error == NULL;
160       free (error);
161
162       if (ok && v_out.f == number)
163         {
164           syntax_gen_string (output, ss_cstr (s));
165           free (s);
166           return;
167         }
168       free (s);
169     }
170
171   if (number == SYSMIS)
172     ds_put_cstr (output, "SYSMIS");
173   else
174     {
175       /* FIXME: This should consistently yield precisely the same
176          value as NUMBER on input, but its results for values
177          cannot be exactly represented in decimal are ugly: many
178          of them will have far more decimal digits than are
179          needed.  The free-format floating point output routine
180          from Steele and White, "How to Print Floating-Point
181          Numbers Accurately" is really what we want.  The MPFR
182          library has an implementation of this, or equivalent
183          functionality, in its mpfr_strtofr routine, but it would
184          not be nice to make PSPP depend on this.  Probably, we
185          should implement something equivalent to it. */
186       ds_put_format (output, "%.*g", DBL_DIG + 1, number);
187     }
188 }
189
190 /* Appends to OUTPUT a representation of VALUE, which has the
191    specified WIDTH.  If FORMAT is non-null, it influences the
192    output format.  The representation is precise, that is, when
193    PSPP parses the representation, its value will be exactly
194    VALUE. */
195 void
196 syntax_gen_value (struct string *output, const union value *value, int width,
197                   const struct fmt_spec *format)
198 {
199   assert (format == NULL || fmt_var_width (format) == width);
200   if (width == 0)
201     syntax_gen_number (output, value->f, format);
202   else
203     {
204       char *s = CHAR_CAST_BUG (char *, value_str (value, width));
205       syntax_gen_string (output, ss_buffer (s, width));
206     }
207 }
208
209 /* Appends <low> THRU <high> to OUTPUT.  If LOW is LOWEST, then
210    it is formatted as the identifier LO; if HIGH is HIGHEST, then
211    it is formatted as the identifier HI.  Otherwise, LOW and HIGH
212    are formatted as with a call to syntax_gen_num with the specified
213    FORMAT.
214
215    This is the opposite of the function parse_num_range. */
216 void
217 syntax_gen_num_range (struct string *output, double low, double high,
218                       const struct fmt_spec *format)
219 {
220   if (low == LOWEST)
221     ds_put_cstr (output, "LO");
222   else
223     syntax_gen_number (output, low, format);
224
225   ds_put_cstr (output, " THRU ");
226
227   if (high == HIGHEST)
228     ds_put_cstr (output, "HI");
229   else
230     syntax_gen_number (output, high, format);
231 }
232
233 /* Same as syntax_gen_pspp, below, but takes a va_list. */
234 void
235 syntax_gen_pspp_valist (struct string *output, const char *format,
236                         va_list args)
237 {
238   for (;;)
239     {
240       size_t copy = strcspn (format, "%");
241       ds_put_substring (output, ss_buffer (format, copy));
242       format += copy;
243
244       if (*format == '\0')
245         return;
246       assert (*format == '%');
247       format++;
248       switch (*format++)
249         {
250         case 's':
251           {
252             const char *s = va_arg (args, char *);
253             switch (*format++)
254               {
255               case 'q':
256                 syntax_gen_string (output, ss_cstr (s));
257                 break;
258               case 's':
259                 ds_put_cstr (output, s);
260                 break;
261               default:
262                 NOT_REACHED ();
263               }
264           }
265           break;
266
267         case 'd':
268           {
269             int i = va_arg (args, int);
270             ds_put_format (output, "%d", i);
271           }
272           break;
273
274         case 'f':
275           {
276             double d = va_arg (args, double);
277             switch (*format++)
278               {
279               case 'p':
280                 ds_put_format (output, "%f", d);
281                 break;
282               default:
283                 NOT_REACHED ();
284               }
285             break;
286           }
287
288         case '%':
289           ds_put_byte (output, '%');
290           break;
291
292         default:
293           NOT_REACHED ();
294         }
295     }
296 }
297
298 /* printf-like function specialized for outputting PSPP syntax.
299    FORMAT is appended to OUTPUT.  The following substitutions are
300    supported:
301
302      %sq: The char * argument is formatted as a PSPP string, as
303           if with a call to syntax_gen_string.
304
305      %ss: The char * argument is appended literally.
306
307      %d: Same as printf's %d.
308
309      %fp: The double argument is formatted precisely as a PSPP
310           number, as if with a call to syntax_gen_number with a
311           null FORMAT argument.
312
313      %%: Literal %.
314
315    (These substitutions were chosen to allow GCC to check for
316    correct argument types.)
317
318    This function is somewhat experimental.  If it proves useful,
319    the allowed substitutions will almost certainly be
320    expanded. */
321 void
322 syntax_gen_pspp (struct string *output, const char *format, ...)
323 {
324   va_list args;
325   va_start (args, format);
326   syntax_gen_pspp_valist (output, format, args);
327   va_end (args);
328 }