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