5ee7ace500911893f24b559a967bd78812793f99
[pspp-builds.git] / src / format.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include "format.h"
22 #include <ctype.h>
23 #include "error.h"
24 #include <stdlib.h>
25 #include "error.h"
26 #include "lexer.h"
27 #include "misc.h"
28 #include "str.h"
29 #include "var.h"
30
31 #include "gettext.h"
32 #define _(msgid) gettext (msgid)
33
34 #define DEFFMT(LABEL, NAME, N_ARGS, IMIN_W, IMAX_W, OMIN_W, OMAX_W, CAT, \
35                OUTPUT, SPSS_FMT) \
36         {NAME, N_ARGS, IMIN_W, IMAX_W, OMIN_W, OMAX_W, CAT, OUTPUT, SPSS_FMT},
37 struct fmt_desc formats[FMT_NUMBER_OF_FORMATS + 1] =
38 {
39 #include "format.def"
40   {"",         -1, -1,  -1, -1,   -1, 0000, -1, -1},
41 };
42
43 /* Common formats. */
44 const struct fmt_spec f8_2 = {FMT_F, 8, 2};
45
46 /* Converts F to its string representation (for instance, "F8.2") and
47    returns a pointer to a static buffer containing that string. */
48 char *
49 fmt_to_string (const struct fmt_spec *f)
50 {
51   static char buf[32];
52
53   if (formats[f->type].n_args >= 2)
54     sprintf (buf, "%s%d.%d", formats[f->type].name, f->w, f->d);
55   else
56     sprintf (buf, "%s%d", formats[f->type].name, f->w);
57   return buf;
58 }
59
60 /* Does checks in common betwen check_input_specifier() and
61    check_output_specifier() and returns true if so.  Otherwise,
62    emits an error message (if EMIT_ERROR is nonzero) and returns
63    false. */
64 static bool
65 check_common_specifier (const struct fmt_spec *spec, bool emit_error)
66 {
67   struct fmt_desc *f = &formats[spec->type];
68   char *str = fmt_to_string (spec);
69
70   if ((f->cat & FCAT_EVEN_WIDTH) && spec->w % 2)
71     {
72       if (emit_error)
73         msg (SE, _("Format %s specifies an odd width %d, but "
74                    "an even width is required."),
75              str, spec->w);
76       return false;
77     }
78   if (f->n_args > 1 && (spec->d < 0 || spec->d > 16))
79     {
80       if (emit_error)
81         msg (SE, _("Format %s specifies a bad number of "
82                    "implied decimal places %d.  Input format %s allows "
83                    "up to 16 implied decimal places."), str, spec->d, f->name);
84       return false;
85     }
86   return true;
87 }
88
89 /* Checks whether SPEC is valid as an input format and returns
90    nonzero if so.  Otherwise, emits an error message (if
91    EMIT_ERROR is nonzero) and returns zero. */
92 int
93 check_input_specifier (const struct fmt_spec *spec, int emit_error)
94 {
95   struct fmt_desc *f = &formats[spec->type];
96   char *str = fmt_to_string (spec);
97
98   if (!check_common_specifier (spec, emit_error))
99     return false;
100   if (spec->type == FMT_X)
101     return 1;
102   if (f->cat & FCAT_OUTPUT_ONLY)
103     {
104       if (emit_error)
105         msg (SE, _("Format %s may not be used for input."), f->name);
106       return 0;
107     }
108   if (spec->w < f->Imin_w || spec->w > f->Imax_w)
109     {
110       if (emit_error)
111         msg (SE, _("Input format %s specifies a bad width %d.  "
112                    "Format %s requires a width between %d and %d."),
113              str, spec->w, f->name, f->Imin_w, f->Imax_w);
114       return 0;
115     }
116   if ((spec->type == FMT_F || spec->type == FMT_COMMA
117           || spec->type == FMT_DOLLAR)
118       && spec->d > spec->w)
119     {
120       if (emit_error)
121         msg (SE, _("Input format %s is invalid because it specifies more "
122                    "decimal places than the field width."), str);
123       return 0;
124     }
125   return 1;
126 }
127
128 /* Checks whether SPEC is valid as an output format and returns
129    nonzero if so.  Otherwise, emits an error message (if
130    EMIT_ERROR is nonzero) and returns zero. */
131 int
132 check_output_specifier (const struct fmt_spec *spec, int emit_error)
133 {
134   struct fmt_desc *f = &formats[spec->type];
135   char *str = fmt_to_string (spec);
136
137   if (!check_common_specifier (spec, emit_error))
138     return false;
139   if (spec->type == FMT_X)
140     return 1;
141   if (spec->w < f->Omin_w || spec->w > f->Omax_w)
142     {
143       if (emit_error)
144         msg (SE, _("Output format %s specifies a bad width %d.  "
145                    "Format %s requires a width between %d and %d."),
146              str, spec->w, f->name, f->Omin_w, f->Omax_w);
147       return 0;
148     }
149   if ((spec->type == FMT_F || spec->type == FMT_COMMA
150           || spec->type == FMT_DOLLAR)
151       && spec->d >= spec->w)
152     {
153       if (emit_error)
154         msg (SE, _("Output format %s is invalid because it specifies as "
155                    "many decimal places as the field width, which "
156                    "fails to allow space for a decimal point.  "
157                    "Try %s%d.%d instead."),
158              str, f->name, spec->d + 1, spec->d);
159       return 0;
160     }
161   return 1;
162 }
163
164 /* Checks that FORMAT is appropriate for a variable of the given
165    TYPE and returns true if so.  Otherwise returns false and (if
166    EMIT_ERROR is true) emits an error message. */
167 bool
168 check_specifier_type (const struct fmt_spec *format,
169                       int type, bool emit_error) 
170 {
171   const struct fmt_desc *f = &formats[format->type];
172   assert (type == NUMERIC || type == ALPHA);
173   if ((type == ALPHA) != ((f->cat & FCAT_STRING) != 0))
174     {
175       if (emit_error)
176         msg (SE, _("%s variables are not compatible with %s format %s."),
177              type == ALPHA ? _("String") : _("Numeric"),
178              type == ALPHA ? _("numeric") : _("string"),
179              fmt_to_string (format));
180       return false;
181     }
182   return true;
183 }
184   
185 /* Checks that FORMAT is appropriate for a variable of the given
186    WIDTH and returns true if so.  Otherwise returns false and (if
187    EMIT_ERROR is true) emits an error message. */
188 bool
189 check_specifier_width (const struct fmt_spec *format,
190                        int width, bool emit_error) 
191 {
192   if (!check_specifier_type (format, width != 0 ? ALPHA : NUMERIC, emit_error))
193     return false;
194   if (get_format_var_width (format) != width)
195     {
196       if (emit_error)
197         msg (SE, _("String variable with width %d not compatible with "
198                    "format %s."),
199              width, fmt_to_string (format));
200       return false;
201     }
202   return true;
203 }
204
205 /* Converts input format specifier INPUT into output format
206    specifier OUTPUT. */
207 void
208 convert_fmt_ItoO (const struct fmt_spec *input, struct fmt_spec *output)
209 {
210   assert (check_input_specifier (input, 0));
211
212   output->type = formats[input->type].output;
213   output->w = input->w;
214   if (output->w > formats[output->type].Omax_w)
215     output->w = formats[output->type].Omax_w;
216   output->d = input->d;
217
218   switch (input->type)
219     {
220     case FMT_F:
221     case FMT_N:
222       if (output->d > 0)
223         output->w++;
224       break;
225     case FMT_E:
226       output->w = max (max (input->w, input->d+7), 10);
227       output->d = max (input->d, 3);
228       break;
229     case FMT_COMMA:
230     case FMT_DOT:
231       /* nothing is necessary */
232       break;
233     case FMT_DOLLAR:
234     case FMT_PCT:
235       if (output->w < 2)
236         output->w = 2;
237       break;
238     case FMT_PIBHEX:
239       {
240         static const int map[] = {4, 6, 9, 11, 14, 16, 18, 21};
241         assert (input->w % 2 == 0 && input->w >= 2 && input->w <= 16);
242         output->w = map[input->w / 2 - 1];
243         break;
244       }
245     case FMT_RBHEX:
246       output->w = 8, output->d = 2;     /* FIXME */
247       break;
248     case FMT_IB:
249     case FMT_PIB:
250     case FMT_P:
251     case FMT_PK:
252     case FMT_RB:
253       if (input->d < 1)
254         output->w = 8, output->d = 2;
255       else
256         output->w = 9 + input->d;
257       break;
258     case FMT_CCA:
259     case FMT_CCB:
260     case FMT_CCC:
261     case FMT_CCD:
262     case FMT_CCE:
263       assert (0);
264     case FMT_Z:
265     case FMT_A:
266       /* nothing is necessary */
267       break;
268     case FMT_AHEX:
269       output->w = input->w / 2;
270       break;
271     case FMT_DATE:
272     case FMT_EDATE:
273     case FMT_SDATE:
274     case FMT_ADATE:
275     case FMT_JDATE:
276       /* nothing is necessary */
277       break;
278     case FMT_QYR:
279       if (output->w < 6)
280         output->w = 6;
281       break;
282     case FMT_MOYR:
283       /* nothing is necessary */
284       break;
285     case FMT_WKYR:
286       if (output->w < 8)
287         output->w = 8;
288       break;
289     case FMT_TIME:
290     case FMT_DTIME:
291     case FMT_DATETIME:
292     case FMT_WKDAY:
293     case FMT_MONTH:
294       /* nothing is necessary */
295       break;
296     default:
297       assert (0);
298     }
299
300   assert (check_output_specifier (output, 0));
301 }
302
303 /* Returns the width corresponding to the format specifier.  The
304    return value is the value of the `width' member of a `struct
305    variable' for such an input format. */
306 int
307 get_format_var_width (const struct fmt_spec *spec) 
308 {
309   if (spec->type == FMT_AHEX)
310     return spec->w / 2;
311   else if (spec->type == FMT_A)
312     return spec->w;
313   else
314     return 0;
315 }
316
317 /* Returns the PSPP format corresponding to the given SPSS
318    format. */
319 int
320 translate_fmt (int spss) 
321 {
322   int type;
323   
324   for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++)
325     if (formats[type].spss == spss)
326       return type;
327   return -1;
328 }
329
330 /* Returns an input format specification with type TYPE, width W,
331    and D decimals. */
332 struct fmt_spec
333 make_input_format (int type, int w, int d) 
334 {
335   struct fmt_spec f;
336   f.type = type;
337   f.w = w;
338   f.d = d;
339   assert (check_input_specifier (&f, 0));
340   return f;
341 }
342
343 /* Returns an output format specification with type TYPE, width
344    W, and D decimals. */
345 struct fmt_spec
346 make_output_format (int type, int w, int d)
347 {
348   struct fmt_spec f;
349   f.type = type;
350   f.w = w;
351   f.d = d;
352   assert (check_output_specifier (&f, 0));
353   return f;
354 }