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