syntax-gen.c : Allow conversion specifiers of the form %.20f
[pspp] / src / ui / syntax-gen.c
1 /* PSPPIRE - a graphical user interface for PSPP.
2    Copyright (C) 2008, 2010, 2011 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/i18n.h"
31 #include "libpspp/message.h"
32 #include "libpspp/str.h"
33 #include "libpspp/misc.h"
34
35 #include "gl/ftoastr.h"
36
37 /* Appends to OUTPUT a pair of hex digits for each byte in IN. */
38 static void
39 syntax_gen_hex_digits (struct string *output, struct substring in)
40 {
41   size_t i;
42   for (i = 0; i < in.length; i++)
43     {
44       unsigned char c = in.string[i];
45       ds_put_byte (output, "0123456789ABCDEF"[c >> 4]);
46       ds_put_byte (output, "0123456789ABCDEF"[c & 0xf]);
47     }
48 }
49
50 /* Returns true if IN contains any control characters, false
51    otherwise */
52 static bool
53 has_control_chars (struct substring in)
54 {
55   size_t i;
56
57   for (i = 0; i < in.length; i++)
58     if (iscntrl ((unsigned char) in.string[i]))
59       return true;
60   return false;
61 }
62
63 static bool
64 has_single_quote (struct substring str)
65 {
66   return (SIZE_MAX != ss_find_byte (str, '\''));
67 }
68
69 static bool
70 has_double_quote (struct substring str)
71 {
72   return (SIZE_MAX != ss_find_byte (str, '"'));
73 }
74
75 /* Appends to OUTPUT valid PSPP syntax for a quoted string that
76    contains IN.
77
78    IN must be encoded in UTF-8, and the quoted result will also
79    be encoded in UTF-8.
80
81    The string will be output as a regular quoted string unless it
82    contains control characters, in which case it is output as a
83    hex string. */
84 void
85 syntax_gen_string (struct string *output, struct substring in)
86 {
87   if (has_control_chars (in))
88     {
89       ds_put_cstr (output, "X'");
90       syntax_gen_hex_digits (output, in);
91       ds_put_byte (output, '\'');
92     }
93   else
94     {
95       int quote;
96       size_t i;
97
98       /* This seemingly simple implementation is possible, because UTF-8
99          guarantees that bytes corresponding to basic characters (such as
100          '\'') cannot appear in a multi-byte character sequence except to
101          represent that basic character.
102       */
103       assert (is_basic ('\''));
104
105       quote = has_double_quote (in) && !has_single_quote (in) ? '\'' : '"';
106       ds_put_byte (output, quote);
107       for (i = 0; i < in.length; i++)
108         {
109           char c = in.string[i];
110           if (c == quote)
111             ds_put_byte (output, quote);
112           ds_put_byte (output, c);
113         }
114       ds_put_byte (output, quote);
115     }
116 }
117
118 /* Appends to OUTPUT a representation of NUMBER in PSPP syntax.
119    The representation is precise, that is, when PSPP parses the
120    representation, its value will be exactly NUMBER.  (This might
121    not be the case on a C implementation where double has a
122    different representation.)
123
124    If NUMBER is the system-missing value, it is output as the
125    identifier SYSMIS.  This may not be appropriate, because
126    SYSMIS is not consistently parsed throughout PSPP syntax as
127    the system-missing value.  But in such circumstances the
128    system-missing value would not be meaningful anyhow, so the
129    caller should refrain from supplying the system-missing value
130    in such cases.
131
132    A value of LOWEST or HIGHEST is not treated specially.
133
134    If FORMAT is null, then the representation will be in numeric
135    form, e.g. 123 or 1.23e10.
136
137    If FORMAT is non-null, then it must point to a numeric format.
138    If the format is one easier for a user to understand when
139    expressed as a string than as a number (for example, a date
140    format), and the string representation precisely represents
141    NUMBER, then the string representation is written to OUTPUT.
142    Otherwise, NUMBER is output as if FORMAT was a null
143    pointer. */
144 void
145 syntax_gen_number (struct string *output,
146                    double number, const struct fmt_spec *format)
147 {
148   assert (format == NULL || fmt_is_numeric (format->type));
149   if (format != NULL
150       && (format->type
151           & (FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT)))
152     {
153       union value v_in, v_out;
154       char *s, *error;
155       bool ok;
156
157       v_in.f = number;
158       s = data_out (&v_in, "FIXME",  format);
159
160       /* FIXME: UTF8 encoded strings will fail here */
161       error = data_in (ss_cstr (s), C_ENCODING, format->type, &v_out, 0, NULL);
162       ok = error == NULL;
163       free (error);
164
165       if (ok && v_out.f == number)
166         {
167           syntax_gen_string (output, ss_cstr (s));
168           free (s);
169           return;
170         }
171       free (s);
172     }
173
174   if (number == SYSMIS)
175     ds_put_cstr (output, "SYSMIS");
176   else
177     {
178       char s[DBL_BUFSIZE_BOUND];
179
180       c_dtoastr (s, sizeof s, 0, 0, number);
181       ds_put_cstr (output, s);
182     }
183 }
184
185 /* Appends to OUTPUT a representation of VALUE, which has the
186    specified WIDTH.  If FORMAT is non-null, it influences the
187    output format.  The representation is precise, that is, when
188    PSPP parses the representation, its value will be exactly
189    VALUE. */
190 void
191 syntax_gen_value (struct string *output, const union value *value, int width,
192                   const struct fmt_spec *format)
193 {
194   assert (format == NULL || fmt_var_width (format) == width);
195   if (width == 0)
196     syntax_gen_number (output, value->f, format);
197   else
198     {
199       char *s = CHAR_CAST_BUG (char *, value_str (value, width));
200       syntax_gen_string (output, ss_buffer (s, width));
201     }
202 }
203
204 /* Appends <low> THRU <high> to OUTPUT.  If LOW is LOWEST, then
205    it is formatted as the identifier LO; if HIGH is HIGHEST, then
206    it is formatted as the identifier HI.  Otherwise, LOW and HIGH
207    are formatted as with a call to syntax_gen_num with the specified
208    FORMAT.
209
210    This is the opposite of the function parse_num_range. */
211 void
212 syntax_gen_num_range (struct string *output, double low, double high,
213                       const struct fmt_spec *format)
214 {
215   if (low == LOWEST)
216     ds_put_cstr (output, "LO");
217   else
218     syntax_gen_number (output, low, format);
219
220   ds_put_cstr (output, " THRU ");
221
222   if (high == HIGHEST)
223     ds_put_cstr (output, "HI");
224   else
225     syntax_gen_number (output, high, format);
226 }
227
228 /* Same as syntax_gen_pspp, below, but takes a va_list. */
229 void
230 syntax_gen_pspp_valist (struct string *output, const char *format,
231                         va_list args)
232 {
233   for (;;)
234     {
235       char qualifier[16];
236       int precision = -1;
237       char directive;
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       directive = *format++;
247       if (directive == '.')
248         {
249           int x = 0;
250           while (directive = *format++, c_isdigit (directive))
251             {
252               assert (x < 16);
253               qualifier[x++] = directive;
254             }
255           qualifier[x++] = '\0';
256           precision = atoi (qualifier);
257         }
258       switch (directive)
259         {
260         case 's':
261           {
262             const char *s = va_arg (args, char *);
263             switch (*format++)
264               {
265               case 'q':
266                 syntax_gen_string (output, ss_cstr (s));
267                 break;
268               case 's':
269                 ds_put_cstr (output, s);
270                 break;
271               default:
272                 NOT_REACHED ();
273               }
274           }
275           break;
276
277         case 'd':
278           {
279             int i = va_arg (args, int);
280             ds_put_format (output, "%d", i);
281           }
282           break;
283
284         case 'f':
285         case 'g':
286           {
287             char conv[32];
288             double d = va_arg (args, double);
289             int x = 0;
290             conv[x++] = '%';
291             conv[x] = '\0';
292             if (precision != -1)
293               {
294                 strcat (conv, ".");
295                 strcat (conv, qualifier);
296                 x += strlen (qualifier) + 1;
297               }
298             conv[x++] = directive;
299             conv[x++] = '\0';
300             
301             ds_put_c_format (output, conv, d);
302             break;
303           }
304
305         case '%':
306           ds_put_byte (output, '%');
307           break;
308
309         default:
310           NOT_REACHED ();
311         }
312     }
313 }
314
315 /* printf-like function specialized for outputting PSPP syntax.
316    FORMAT is appended to OUTPUT.  The following substitutions are
317    supported:
318
319      %sq: The char * argument is formatted as a PSPP string, as
320           if with a call to syntax_gen_string.
321
322      %ss: The char * argument is appended literally.
323
324      %d: Same as printf's %d.
325
326      %f %g: Same as printf.
327
328      %%: Literal %.
329
330    (These substitutions were chosen to allow GCC to check for
331    correct argument types.)
332
333    This function is somewhat experimental.  If it proves useful,
334    the allowed substitutions will almost certainly be
335    expanded. */
336 void
337 syntax_gen_pspp (struct string *output, const char *format, ...)
338 {
339   va_list args;
340   va_start (args, format);
341   syntax_gen_pspp_valist (output, format, args);
342   va_end (args);
343 }