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