Adopt use of gnulib for portability.
[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 /* Parses the alphabetic prefix of the current token as a format
47    specifier name.  Returns the corresponding format specifier
48    type if successful, or -1 on failure.  If ALLOW_XT is zero,
49    then X and T format specifiers are not allowed.  If CP is
50    nonzero, then *CP is set to the first non-alphabetic character
51    in the current token on success or to a null pointer on
52    failure. */
53 int
54 parse_format_specifier_name (const char **cp, enum fmt_parse_flags flags)
55 {
56   char *sp, *ep;
57   int idx;
58
59   sp = ep = ds_c_str (&tokstr);
60   while (isalpha ((unsigned char) *ep))
61     ep++;
62
63   if (sp != ep) 
64     {
65       /* Find format. */
66       for (idx = 0; idx < FMT_NUMBER_OF_FORMATS; idx++)
67         if (strlen (formats[idx].name) == ep - sp
68             && !buf_compare_case (formats[idx].name, sp, ep - sp))
69           break;
70
71       /* Check format. */
72       if (idx < FMT_NUMBER_OF_FORMATS)
73         {
74           if (!(flags & FMTP_ALLOW_XT) && (idx == FMT_T || idx == FMT_X)) 
75             {
76               if (!(flags & FMTP_SUPPRESS_ERRORS))
77                 msg (SE, _("X and T format specifiers not allowed here."));
78               idx = -1; 
79             }
80         }
81       else 
82         {
83           /* No match. */
84           if (!(flags & FMTP_SUPPRESS_ERRORS))
85             msg (SE, _("%.*s is not a valid data format."),
86                  (int) (ep - sp), ds_c_str (&tokstr));
87           idx = -1; 
88         }
89     }
90   else 
91     {
92       lex_error ("expecting data format");
93       idx = -1;
94     }
95       
96   if (cp != NULL) 
97     {
98       if (idx != -1)
99         *cp = ep;
100       else
101         *cp = NULL;
102     }
103
104   return idx;
105 }
106
107 /* Converts F to its string representation (for instance, "F8.2") and
108    returns a pointer to a static buffer containing that string. */
109 char *
110 fmt_to_string (const struct fmt_spec *f)
111 {
112   static char buf[32];
113
114   if (formats[f->type].n_args >= 2)
115     sprintf (buf, "%s%d.%d", formats[f->type].name, f->w, f->d);
116   else
117     sprintf (buf, "%s%d", formats[f->type].name, f->w);
118   return buf;
119 }
120
121 /* Does checks in common betwen check_input_specifier() and
122    check_output_specifier() and returns true if so.  Otherwise,
123    emits an error message (if EMIT_ERROR is nonzero) and returns
124    false. */
125 static bool
126 check_common_specifier (const struct fmt_spec *spec, bool emit_error)
127 {
128   struct fmt_desc *f = &formats[spec->type];
129   char *str = fmt_to_string (spec);
130
131   if ((f->cat & FCAT_EVEN_WIDTH) && spec->w % 2)
132     {
133       if (emit_error)
134         msg (SE, _("Format %s specifies an odd width %d, but "
135                    "an even width is required."),
136              str, spec->w);
137       return false;
138     }
139   if (f->n_args > 1 && (spec->d < 0 || spec->d > 16))
140     {
141       if (emit_error)
142         msg (SE, _("Format %s specifies a bad number of "
143                    "implied decimal places %d.  Input format %s allows "
144                    "up to 16 implied decimal places."), str, spec->d, f->name);
145       return false;
146     }
147   return true;
148 }
149
150 /* Checks whether SPEC is valid as an input format and returns
151    nonzero if so.  Otherwise, emits an error message (if
152    EMIT_ERROR is nonzero) and returns zero. */
153 int
154 check_input_specifier (const struct fmt_spec *spec, int emit_error)
155 {
156   struct fmt_desc *f = &formats[spec->type];
157   char *str = fmt_to_string (spec);
158
159   if (!check_common_specifier (spec, emit_error))
160     return false;
161   if (spec->type == FMT_X)
162     return 1;
163   if (f->cat & FCAT_OUTPUT_ONLY)
164     {
165       if (emit_error)
166         msg (SE, _("Format %s may not be used for input."), f->name);
167       return 0;
168     }
169   if (spec->w < f->Imin_w || spec->w > f->Imax_w)
170     {
171       if (emit_error)
172         msg (SE, _("Input format %s specifies a bad width %d.  "
173                    "Format %s requires a width between %d and %d."),
174              str, spec->w, f->name, f->Imin_w, f->Imax_w);
175       return 0;
176     }
177   if ((spec->type == FMT_F || spec->type == FMT_COMMA
178           || spec->type == FMT_DOLLAR)
179       && spec->d > spec->w)
180     {
181       if (emit_error)
182         msg (SE, _("Input format %s is invalid because it specifies more "
183                    "decimal places than the field width."), str);
184       return 0;
185     }
186   return 1;
187 }
188
189 /* Checks whether SPEC is valid as an output format and returns
190    nonzero if so.  Otherwise, emits an error message (if
191    EMIT_ERROR is nonzero) and returns zero. */
192 int
193 check_output_specifier (const struct fmt_spec *spec, int emit_error)
194 {
195   struct fmt_desc *f = &formats[spec->type];
196   char *str = fmt_to_string (spec);
197
198   if (!check_common_specifier (spec, emit_error))
199     return false;
200   if (spec->type == FMT_X)
201     return 1;
202   if (spec->w < f->Omin_w || spec->w > f->Omax_w)
203     {
204       if (emit_error)
205         msg (SE, _("Output format %s specifies a bad width %d.  "
206                    "Format %s requires a width between %d and %d."),
207              str, spec->w, f->name, f->Omin_w, f->Omax_w);
208       return 0;
209     }
210   if ((spec->type == FMT_F || spec->type == FMT_COMMA
211           || spec->type == FMT_DOLLAR)
212       && spec->d >= spec->w)
213     {
214       if (emit_error)
215         msg (SE, _("Output format %s is invalid because it specifies as "
216                    "many decimal places as the field width, which "
217                    "fails to allow space for a decimal point.  "
218                    "Try %s%d.%d instead."),
219              str, f->name, spec->d + 1, spec->d);
220       return 0;
221     }
222   return 1;
223 }
224
225 /* Checks that FORMAT is appropriate for a variable of the given
226    TYPE and returns true if so.  Otherwise returns false and (if
227    EMIT_ERROR is true) emits an error message. */
228 bool
229 check_specifier_type (const struct fmt_spec *format,
230                       int type, bool emit_error) 
231 {
232   const struct fmt_desc *f = &formats[format->type];
233   assert (type == NUMERIC || type == ALPHA);
234   if ((type == ALPHA) != ((f->cat & FCAT_STRING) != 0))
235     {
236       if (emit_error)
237         msg (SE, _("%s variables are not compatible with %s format %s."),
238              type == ALPHA ? _("String") : _("Numeric"),
239              type == ALPHA ? _("numeric") : _("string"),
240              fmt_to_string (format));
241       return false;
242     }
243   return true;
244 }
245   
246 /* Checks that FORMAT is appropriate for a variable of the given
247    WIDTH and returns true if so.  Otherwise returns false and (if
248    EMIT_ERROR is true) emits an error message. */
249 bool
250 check_specifier_width (const struct fmt_spec *format,
251                        int width, bool emit_error) 
252 {
253   if (!check_specifier_type (format, width != 0 ? ALPHA : NUMERIC, emit_error))
254     return false;
255   if (get_format_var_width (format) != width)
256     {
257       if (emit_error)
258         msg (SE, _("String variable with width %d not compatible with "
259                    "format %s."),
260              width, fmt_to_string (format));
261       return false;
262     }
263   return true;
264 }
265
266 /* Converts input format specifier INPUT into output format
267    specifier OUTPUT. */
268 void
269 convert_fmt_ItoO (const struct fmt_spec *input, struct fmt_spec *output)
270 {
271   assert (check_input_specifier (input, 0));
272
273   output->type = formats[input->type].output;
274   output->w = input->w;
275   if (output->w > formats[output->type].Omax_w)
276     output->w = formats[output->type].Omax_w;
277   output->d = input->d;
278
279   switch (input->type)
280     {
281     case FMT_F:
282     case FMT_N:
283       if (output->d > 0)
284         output->w++;
285       break;
286     case FMT_E:
287       output->w = max (max (input->w, input->d+7), 10);
288       output->d = max (input->d, 3);
289       break;
290     case FMT_COMMA:
291     case FMT_DOT:
292       /* nothing is necessary */
293       break;
294     case FMT_DOLLAR:
295     case FMT_PCT:
296       if (output->w < 2)
297         output->w = 2;
298       break;
299     case FMT_PIBHEX:
300       {
301         static const int map[] = {4, 6, 9, 11, 14, 16, 18, 21};
302         assert (input->w % 2 == 0 && input->w >= 2 && input->w <= 16);
303         output->w = map[input->w / 2 - 1];
304         break;
305       }
306     case FMT_RBHEX:
307       output->w = 8, output->d = 2;     /* FIXME */
308       break;
309     case FMT_IB:
310     case FMT_PIB:
311     case FMT_P:
312     case FMT_PK:
313     case FMT_RB:
314       if (input->d < 1)
315         output->w = 8, output->d = 2;
316       else
317         output->w = 9 + input->d;
318       break;
319     case FMT_CCA:
320     case FMT_CCB:
321     case FMT_CCC:
322     case FMT_CCD:
323     case FMT_CCE:
324       assert (0);
325     case FMT_Z:
326     case FMT_A:
327       /* nothing is necessary */
328       break;
329     case FMT_AHEX:
330       output->w = input->w / 2;
331       break;
332     case FMT_DATE:
333     case FMT_EDATE:
334     case FMT_SDATE:
335     case FMT_ADATE:
336     case FMT_JDATE:
337       /* nothing is necessary */
338       break;
339     case FMT_QYR:
340       if (output->w < 6)
341         output->w = 6;
342       break;
343     case FMT_MOYR:
344       /* nothing is necessary */
345       break;
346     case FMT_WKYR:
347       if (output->w < 8)
348         output->w = 8;
349       break;
350     case FMT_TIME:
351     case FMT_DTIME:
352     case FMT_DATETIME:
353     case FMT_WKDAY:
354     case FMT_MONTH:
355       /* nothing is necessary */
356       break;
357     default:
358       assert (0);
359     }
360
361   assert (check_output_specifier (output, 0));
362 }
363
364 /* Parses a format specifier from the token stream and returns
365    nonzero only if successful.  Emits an error message on
366    failure.  Allows X and T format specifiers only if ALLOW_XT is
367    nonzero.  The caller should call check_input_specifier() or
368    check_output_specifier() on the parsed format as
369    necessary.  */
370 int
371 parse_format_specifier (struct fmt_spec *input, enum fmt_parse_flags flags)
372 {
373   struct fmt_spec spec;
374   struct fmt_desc *f;
375   const char *cp;
376   char *cp2;
377   int type, w, d;
378
379   if (token != T_ID)
380     {
381       if (!(flags & FMTP_SUPPRESS_ERRORS))
382         msg (SE, _("Format specifier expected."));
383       return 0;
384     }
385   type = parse_format_specifier_name (&cp, flags);
386   if (type == -1)
387     return 0;
388   f = &formats[type];
389
390   w = strtol (cp, &cp2, 10);
391   if (cp2 == cp && type != FMT_X)
392     {
393       if (!(flags & FMTP_SUPPRESS_ERRORS))
394         msg (SE, _("Data format %s does not specify a width."),
395              ds_c_str (&tokstr));
396       return 0;
397     }
398
399   cp = cp2;
400   if (f->n_args > 1 && *cp == '.')
401     {
402       cp++;
403       d = strtol (cp, &cp2, 10);
404       cp = cp2;
405     }
406   else
407     d = 0;
408
409   if (*cp)
410     {
411       if (!(flags & FMTP_SUPPRESS_ERRORS))
412         msg (SE, _("Data format %s is not valid."), ds_c_str (&tokstr));
413       return 0;
414     }
415   lex_get ();
416
417   spec.type = type;
418   spec.w = w;
419   spec.d = d;
420   *input = spec;
421
422   return 1;
423 }
424
425 /* Returns the width corresponding to the format specifier.  The
426    return value is the value of the `width' member of a `struct
427    variable' for such an input format. */
428 int
429 get_format_var_width (const struct fmt_spec *spec) 
430 {
431   if (spec->type == FMT_AHEX)
432     return spec->w / 2;
433   else if (spec->type == FMT_A)
434     return spec->w;
435   else
436     return 0;
437 }
438
439 /* Returns the PSPP format corresponding to the given SPSS
440    format. */
441 int
442 translate_fmt (int spss) 
443 {
444   int type;
445   
446   for (type = 0; type < FMT_NUMBER_OF_FORMATS; type++)
447     if (formats[type].spss == spss)
448       return type;
449   return -1;
450 }
451
452 /* Returns an input format specification with type TYPE, width W,
453    and D decimals. */
454 struct fmt_spec
455 make_input_format (int type, int w, int d) 
456 {
457   struct fmt_spec f;
458   f.type = type;
459   f.w = w;
460   f.d = d;
461   assert (check_input_specifier (&f, 0));
462   return f;
463 }
464
465 /* Returns an output format specification with type TYPE, width
466    W, and D decimals. */
467 struct fmt_spec
468 make_output_format (int type, int w, int d)
469 {
470   struct fmt_spec f;
471   f.type = type;
472   f.w = w;
473   f.d = d;
474   assert (check_output_specifier (&f, 0));
475   return f;
476 }