50f884a3ff2dbba7b28b21f0253dba80a6865fb1
[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 directive;
236       size_t copy = strcspn (format, "%");
237       ds_put_substring (output, ss_buffer (format, copy));
238       format += copy;
239
240       if (*format == '\0')
241         return;
242       assert (*format == '%');
243       format++;
244       directive = *format++;
245       switch (directive)
246         {
247         case 's':
248           {
249             const char *s = va_arg (args, char *);
250             switch (*format++)
251               {
252               case 'q':
253                 syntax_gen_string (output, ss_cstr (s));
254                 break;
255               case 's':
256                 ds_put_cstr (output, s);
257                 break;
258               default:
259                 NOT_REACHED ();
260               }
261           }
262           break;
263
264         case 'd':
265           {
266             int i = va_arg (args, int);
267             ds_put_format (output, "%d", i);
268           }
269           break;
270
271         case 'f':
272         case 'g':
273           {
274             char conv[3];
275             double d = va_arg (args, double);
276             conv[0]='%';
277             conv[1]=directive;
278             conv[2]='\0';
279             ds_put_c_format (output, conv, d);
280             break;
281           }
282
283         case '%':
284           ds_put_byte (output, '%');
285           break;
286
287         default:
288           NOT_REACHED ();
289         }
290     }
291 }
292
293 /* printf-like function specialized for outputting PSPP syntax.
294    FORMAT is appended to OUTPUT.  The following substitutions are
295    supported:
296
297      %sq: The char * argument is formatted as a PSPP string, as
298           if with a call to syntax_gen_string.
299
300      %ss: The char * argument is appended literally.
301
302      %d: Same as printf's %d.
303
304      %f %g: Same as printf.
305
306      %%: Literal %.
307
308    (These substitutions were chosen to allow GCC to check for
309    correct argument types.)
310
311    This function is somewhat experimental.  If it proves useful,
312    the allowed substitutions will almost certainly be
313    expanded. */
314 void
315 syntax_gen_pspp (struct string *output, const char *format, ...)
316 {
317   va_list args;
318   va_start (args, format);
319   syntax_gen_pspp_valist (output, format, args);
320   va_end (args);
321 }