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