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