NPAR: Fix order of arguments to xnmalloc().
[pspp.git] / src / ui / syntax-gen.c
blob039a07d29b0b1e2b001287529ea91718df52cca2
1 /* PSPPIRE - a graphical user interface for PSPP.
2 Copyright (C) 2008, 2010, 2011, 2014 Free Software Foundation, Inc.
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.
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.
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/>. */
17 #include <config.h>
19 #include "ui/syntax-gen.h"
21 #include <ctype.h>
22 #include <mbchar.h>
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"
36 #include "gl/c-ctype.h"
37 #include "gl/ftoastr.h"
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)
43 size_t i;
44 for (i = 0; i < in.length; i++)
46 unsigned char c = in.string[i];
47 ds_put_byte (output, "0123456789ABCDEF"[c >> 4]);
48 ds_put_byte (output, "0123456789ABCDEF"[c & 0xf]);
52 /* Returns true if IN contains any control characters, false
53 otherwise */
54 static bool
55 has_control_chars (struct substring in)
57 size_t i;
59 for (i = 0; i < in.length; i++)
60 if (iscntrl ((unsigned char) in.string[i]))
61 return true;
62 return false;
65 static bool
66 has_single_quote (struct substring str)
68 return (SIZE_MAX != ss_find_byte (str, '\''));
71 static bool
72 has_double_quote (struct substring str)
74 return (SIZE_MAX != ss_find_byte (str, '"'));
77 /* Appends to OUTPUT valid PSPP syntax for a quoted string that
78 contains IN.
80 IN must be encoded in UTF-8, and the quoted result will also
81 be encoded in UTF-8.
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)
89 if (has_control_chars (in))
91 ds_put_cstr (output, "X'");
92 syntax_gen_hex_digits (output, in);
93 ds_put_byte (output, '\'');
95 else
97 int quote;
98 size_t i;
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.
105 assert (is_basic ('\''));
107 quote = has_double_quote (in) && !has_single_quote (in) ? '\'' : '"';
108 ds_put_byte (output, quote);
109 for (i = 0; i < in.length; i++)
111 char c = in.string[i];
112 if (c == quote)
113 ds_put_byte (output, quote);
114 ds_put_byte (output, c);
116 ds_put_byte (output, quote);
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.)
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.
134 A value of LOWEST or HIGHEST is not treated specially.
136 If FORMAT is null, then the representation will be in numeric
137 form, e.g. 123 or 1.23e10.
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)
150 assert (format == NULL || fmt_is_numeric (format->type));
151 if (format != NULL
152 && (fmt_get_category (format->type)
153 & (FMT_CAT_DATE | FMT_CAT_TIME | FMT_CAT_DATE_COMPONENT)))
155 union value v_in, v_out;
156 char *s, *error;
157 bool ok;
159 v_in.f = number;
160 s = data_out (&v_in, "FIXME", *format, settings_get_fmt_settings ());
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);
168 if (ok && v_out.f == number)
170 syntax_gen_string (output, ss_cstr (s));
171 free (s);
172 return;
174 free (s);
177 if (number == SYSMIS)
178 ds_put_cstr (output, "SYSMIS");
179 else
181 char s[DBL_BUFSIZE_BOUND];
183 c_dtoastr (s, sizeof s, 0, 0, number);
184 ds_put_cstr (output, s);
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)
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));
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.
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)
216 if (low == LOWEST)
217 ds_put_cstr (output, "LO");
218 else
219 syntax_gen_number (output, low, format);
221 ds_put_cstr (output, " THRU ");
223 if (high == HIGHEST)
224 ds_put_cstr (output, "HI");
225 else
226 syntax_gen_number (output, high, format);
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)
234 for (;;)
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;
243 if (*format == '\0')
244 return;
245 assert (*format == '%');
246 format++;
247 directive = *format++;
248 if (directive == '.')
250 int x = 0;
251 while (directive = *format++, c_isdigit (directive))
253 assert (x < 16);
254 qualifier[x++] = directive;
256 qualifier[x++] = '\0';
257 precision = atoi (qualifier);
259 switch (directive)
261 case 's':
263 const char *s = va_arg (args, char *);
264 switch (*format++)
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 ();
276 break;
278 case 'd':
280 int i = va_arg (args, int);
281 ds_put_format (output, "%d", i);
283 break;
285 case 'f':
286 case 'g':
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)
295 strcat (conv, ".");
296 strcat (conv, qualifier);
297 x += strlen (qualifier) + 1;
299 conv[x++] = directive;
300 conv[x++] = '\0';
302 ds_put_c_format (output, conv, d);
303 break;
306 case '%':
307 ds_put_byte (output, '%');
308 break;
310 default:
311 NOT_REACHED ();
316 /* printf-like function specialized for outputting PSPP syntax.
317 FORMAT is appended to OUTPUT. The following substitutions are
318 supported:
320 %sq: The char * argument is formatted as a PSPP string, as
321 if with a call to syntax_gen_string.
323 %ss: The char * argument is appended literally.
325 %d: Same as printf's %d.
327 %f %g: Same as printf.
329 %%: Literal %.
331 (These substitutions were chosen to allow GCC to check for
332 correct argument types.)
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, ...)
340 va_list args;
341 va_start (args, format);
342 syntax_gen_pspp_valist (output, format, args);
343 va_end (args);