Change license from GPLv2+ to GPLv3+.
[pspp-builds.git] / src / language / data-io / placement-parser.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2006 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 <language/data-io/placement-parser.h>
20
21 #include <assert.h>
22
23 #include <language/lexer/format-parser.h>
24 #include <language/lexer/lexer.h>
25 #include <libpspp/message.h>
26 #include <libpspp/pool.h>
27 #include <libpspp/str.h>
28
29 #include "xalloc.h"
30 #include "xsize.h"
31
32 #include "gettext.h"
33 #define _(msgid) gettext (msgid)
34
35 /* Extensions to the format specifiers used only for
36    placement. */
37 enum
38   {
39     PRS_TYPE_T = SCHAR_MAX - 3, /* Tab to absolute column. */
40     PRS_TYPE_X,                 /* Skip columns. */
41     PRS_TYPE_NEW_REC            /* Next record. */
42   };
43
44 static bool fixed_parse_columns (struct lexer *, struct pool *, size_t var_cnt, bool for_input,
45                                  struct fmt_spec **, size_t *);
46 static bool fixed_parse_fortran (struct lexer *l, struct pool *, bool for_input,
47                                  struct fmt_spec **, size_t *);
48
49 /* Parses Fortran-like or column-based specifications for placing
50    variable data in fixed positions in columns and rows, that is,
51    formats like those parsed by DATA LIST or PRINT.  Returns true
52    only if successful.
53
54    If successful, formats for VAR_CNT variables are stored in
55    *FORMATS, and the number of formats required is stored in
56    *FORMAT_CNT.  *FORMAT_CNT may be greater than VAR_CNT because
57    of T, X, and / "formats", but success guarantees that exactly
58    VAR_CNT variables will be placed by the output formats.  The
59    caller should call execute_placement_format to process those
60    "formats" in interpreting the output.
61
62    Uses POOL for allocation.  When the caller is finished
63    interpreting *FORMATS, POOL may be destroyed. */
64 bool
65 parse_var_placements (struct lexer *lexer, struct pool *pool, size_t var_cnt, bool for_input,
66                       struct fmt_spec **formats, size_t *format_cnt)
67 {
68   assert (var_cnt > 0);
69   if (lex_is_number (lexer))
70     return fixed_parse_columns (lexer, pool, var_cnt, for_input, formats, format_cnt);
71   else if (lex_match (lexer, '('))
72     {
73       size_t assignment_cnt;
74       size_t i;
75
76       if (!fixed_parse_fortran (lexer, pool, for_input, formats, format_cnt))
77         return false;
78
79       assignment_cnt = 0;
80       for (i = 0; i < *format_cnt; i++)
81         assignment_cnt += (*formats)[i].type < FMT_NUMBER_OF_FORMATS;
82
83       if (assignment_cnt != var_cnt)
84         {
85           msg (SE, _("Number of variables specified (%d) "
86                      "differs from number of variable formats (%d)."),
87                (int) var_cnt, (int) assignment_cnt);
88           return false;
89         }
90
91       return true;
92     }
93   else
94     {
95       msg (SE, _("SPSS-like or Fortran-like format "
96                  "specification expected after variable names."));
97       return false;
98     }
99 }
100
101 /* Implements parse_var_placements for column-based formats. */
102 static bool
103 fixed_parse_columns (struct lexer *lexer, struct pool *pool, size_t var_cnt, bool for_input,
104                      struct fmt_spec **formats, size_t *format_cnt)
105 {
106   struct fmt_spec format;
107   int fc, lc;
108   size_t i;
109
110   if ( !parse_column_range (lexer, &fc, &lc, NULL) )
111     return false;
112
113   /* Divide columns evenly. */
114   format.w = (lc - fc + 1) / var_cnt;
115   if ((lc - fc + 1) % var_cnt)
116     {
117       msg (SE, _("The %d columns %d-%d "
118                  "can't be evenly divided into %u fields."),
119            lc - fc + 1, fc, lc, (unsigned int) var_cnt);
120       return false;
121     }
122
123   /* Format specifier. */
124   if (lex_match (lexer, '('))
125     {
126       /* Get format type. */
127       if (lex_token (lexer) == T_ID)
128         {
129           if (!parse_format_specifier_name (lexer, &format.type))
130             return false;
131           lex_match (lexer, ',');
132         }
133       else
134         format.type = FMT_F;
135
136       /* Get decimal places. */
137       if (lex_is_integer (lexer))
138         {
139           format.d = lex_integer (lexer);
140           lex_get (lexer);
141         }
142       else
143         format.d = 0;
144
145       if (!lex_force_match (lexer, ')'))
146         return false;
147     }
148   else
149     {
150       format.type = FMT_F;
151       format.d = 0;
152     }
153   if (!fmt_check (&format, for_input))
154     return false;
155
156   *formats = pool_nalloc (pool, var_cnt + 1, sizeof **formats);
157   *format_cnt = var_cnt + 1;
158   (*formats)[0].type = PRS_TYPE_T;
159   (*formats)[0].w = fc;
160   for (i = 1; i <= var_cnt; i++)
161     (*formats)[i] = format;
162   return true;
163 }
164
165 /* Implements parse_var_placements for Fortran-like formats. */
166 static bool
167 fixed_parse_fortran (struct lexer *lexer, struct pool *pool, bool for_input,
168                      struct fmt_spec **formats, size_t *format_cnt)
169 {
170   size_t formats_allocated = 0;
171   size_t formats_used = 0;
172
173   *formats = NULL;
174   while (!lex_match (lexer, ')'))
175     {
176       struct fmt_spec f;
177       struct fmt_spec *new_formats;
178       size_t new_format_cnt;
179       size_t count;
180       size_t formats_needed;
181
182       /* Parse count. */
183       if (lex_is_integer (lexer))
184         {
185           count = lex_integer (lexer);
186           lex_get (lexer);
187         }
188       else
189         count = 1;
190
191       /* Parse format specifier. */
192       if (lex_match (lexer, '('))
193         {
194           /* Call ourselves recursively to handle parentheses. */
195           if (!fixed_parse_fortran (lexer, pool, for_input,
196                                     &new_formats, &new_format_cnt))
197             return false;
198         }
199       else
200         {
201           new_formats = &f;
202           new_format_cnt = 1;
203           if (lex_match (lexer, '/'))
204             f.type = PRS_TYPE_NEW_REC;
205           else
206             {
207               char type[FMT_TYPE_LEN_MAX + 1];
208
209               if (!parse_abstract_format_specifier (lexer, type, &f.w, &f.d))
210                 return false;
211
212               if (!strcasecmp (type, "T"))
213                 f.type = PRS_TYPE_T;
214               else if (!strcasecmp (type, "X"))
215                 {
216                   f.type = PRS_TYPE_X;
217                   f.w = count;
218                   count = 1;
219                 }
220               else
221                 {
222                   if (!fmt_from_name (type, &f.type))
223                     {
224                       msg (SE, _("Unknown format type \"%s\"."), type);
225                       return false;
226                     }
227                   if (!fmt_check (&f, for_input))
228                     return false;
229                 }
230             }
231         }
232
233       /* Add COUNT copies of the NEW_FORMAT_CNT formats in
234          NEW_FORMATS to FORMATS. */
235       if (new_format_cnt != 0
236           && size_overflow_p (xtimes (xsum (formats_used,
237                                             xtimes (count, new_format_cnt)),
238                                       sizeof *formats)))
239         xalloc_die ();
240       formats_needed = count * new_format_cnt;
241       if (formats_used + formats_needed > formats_allocated)
242         {
243           formats_allocated = formats_used + formats_needed;
244           *formats = pool_2nrealloc (pool, *formats, &formats_allocated,
245                                      sizeof **formats);
246         }
247       for (; count > 0; count--)
248         {
249           memcpy (&(*formats)[formats_used], new_formats,
250                   sizeof **formats * new_format_cnt);
251           formats_used += new_format_cnt;
252         }
253
254       lex_match (lexer, ',');
255     }
256
257   *format_cnt = formats_used;
258   return true;
259 }
260
261 /* Checks whether FORMAT represents one of the special "formats"
262    for T, X, or /.  If so, updates *RECORD or *COLUMN (or both)
263    as appropriate, and returns true.  Otherwise, returns false
264    without any side effects. */
265 bool
266 execute_placement_format (const struct fmt_spec *format,
267                           int *record, int *column)
268 {
269   switch (format->type)
270     {
271     case PRS_TYPE_X:
272       *column += format->w;
273       return true;
274
275     case PRS_TYPE_T:
276       *column = format->w;
277       return true;
278
279     case PRS_TYPE_NEW_REC:
280       (*record)++;
281       *column = 1;
282       return true;
283
284     default:
285       assert (format->type < FMT_NUMBER_OF_FORMATS);
286       return false;
287     }
288 }
289
290 /* Parse a column or a range of columns, specified as a single
291    integer or two integer delimited by a dash.  Stores the range
292    in *FIRST_COLUMN and *LAST_COLUMN.  (If only a single integer
293    is given, it is stored in both.)  If RANGE_SPECIFIED is
294    non-null, then *RANGE_SPECIFIED is set to true if the syntax
295    contained a dash, false otherwise.  Returns true if
296    successful, false if the syntax was invalid or the values
297    specified did not make sense. */
298 bool
299 parse_column_range (struct lexer *lexer, int *first_column, int *last_column,
300                     bool *range_specified)
301 {
302   /* First column. */
303   if (!lex_force_int (lexer))
304     return false;
305   *first_column = lex_integer (lexer);
306   if (*first_column < 1)
307     {
308       msg (SE, _("Column positions for fields must be positive."));
309       return false;
310     }
311   lex_get (lexer);
312
313   /* Last column. */
314   lex_negative_to_dash (lexer);
315   if (lex_match (lexer, '-'))
316     {
317       if (!lex_force_int (lexer))
318         return false;
319       *last_column = lex_integer (lexer);
320       if (*last_column < 1)
321         {
322           msg (SE, _("Column positions for fields must be positive."));
323           return false;
324         }
325       else if (*last_column < *first_column)
326         {
327           msg (SE, _("The ending column for a field must be "
328                      "greater than the starting column."));
329           return false;
330         }
331
332       if (range_specified)
333         *range_specified = true;
334       lex_get (lexer);
335     }
336   else
337     {
338       *last_column = *first_column;
339       if (range_specified)
340         *range_specified = false;
341     }
342
343   return true;
344 }
345
346 /* Parses a (possibly empty) sequence of slashes, each of which
347    may be followed by an integer.  A slash on its own increases
348    *RECORD by 1 and sets *COLUMN to 1.  A slash followed by an
349    integer sets *RECORD to the integer, as long as that increases
350    *RECORD, and sets *COLUMN to 1.
351
352    Returns true if successful, false on syntax error. */
353 bool
354 parse_record_placement (struct lexer *lexer, int *record, int *column)
355 {
356   while (lex_match (lexer, '/'))
357     {
358       if (lex_is_integer (lexer))
359         {
360           if (lex_integer (lexer) <= *record)
361             {
362               msg (SE, _("The record number specified, %ld, is at or "
363                          "before the previous record, %d.  Data "
364                          "fields must be listed in order of "
365                          "increasing record number."),
366                    lex_integer (lexer), *record);
367               return false;
368             }
369           *record = lex_integer (lexer);
370           lex_get (lexer);
371         }
372       else
373         (*record)++;
374       *column = 1;
375     }
376   assert (*record >= 1);
377
378   return true;
379 }