b8921255d3b08e59b592021b7a025f59f7af1e38
[pspp] / src / output / options.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2010 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 "output/options.h"
20
21 #include <errno.h>
22 #include <limits.h>
23 #include <stdarg.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "libpspp/str.h"
28 #include "libpspp/string-map.h"
29 #include "output/driver-provider.h"
30 #include "output/measure.h"
31
32 #include "gl/xalloc.h"
33
34 #include "gettext.h"
35 #define _(msgid) gettext (msgid)
36
37 /* Creates and returns a new struct driver_option that contains copies of
38    all of the supplied arguments.  All of the arguments must be nonnull,
39    except that VALUE may be NULL (if the user did not supply a value for this
40    option).
41
42    Refer to struct driver_option for the meaning of each argument. */
43 struct driver_option *
44 driver_option_create (const char *driver_name, const char *name,
45                       const char *value, const char *default_value)
46 {
47   struct driver_option *o = xmalloc (sizeof *o);
48   o->driver_name = xstrdup (driver_name);
49   o->name = xstrdup (name);
50   o->value = value != NULL ? xstrdup (value) : NULL;
51   o->default_value = xstrdup (default_value);
52   return o;
53 }
54
55 /* Creates and returns a new struct driver_option for output driver DRIVER
56    (which is needed only to the extent that its name will be used in error
57    messages).  The option named NAME is extracted from OPTIONS.  DEFAULT_VALUE
58    is the default value of the option, used if the given option was not
59    supplied or was invalid. */
60 struct driver_option *
61 driver_option_get (struct output_driver *driver, struct string_map *options,
62                    const char *name, const char *default_value)
63 {
64   struct driver_option *option;
65   char *value;
66
67   value = string_map_find_and_delete (options, name);
68   option = driver_option_create (output_driver_get_name (driver), name, value,
69                                  default_value);
70   free (value);
71   return option;
72 }
73
74 /* Frees driver option O. */
75 void
76 driver_option_destroy (struct driver_option *o)
77 {
78   if (o != NULL)
79     {
80       free (o->driver_name);
81       free (o->name);
82       free (o->value);
83       free (o->default_value);
84       free (o);
85     }
86 }
87
88 /* Stores the paper size of the value of option O into *H and *V, in 1/72000"
89    units.  Any syntax accepted by measure_paper() may be used.
90
91    Destroys O. */
92 void
93 parse_paper_size (struct driver_option *o, int *h, int *v)
94 {
95   if (o->value == NULL || !measure_paper (o->value, h, v))
96     measure_paper (o->default_value, h, v);
97   driver_option_destroy (o);
98 }
99
100 static int
101 do_parse_boolean (const char *driver_name, const char *key,
102                   const char *value)
103 {
104   if (!strcmp (value, "on") || !strcmp (value, "true")
105       || !strcmp (value, "yes") || !strcmp (value, "1"))
106     return true;
107   else if (!strcmp (value, "off") || !strcmp (value, "false")
108            || !strcmp (value, "no") || !strcmp (value, "0"))
109     return false;
110   else
111     {
112       msg (MW, _("%s: `%s' is `%s' but a Boolean value is required"),
113              driver_name, value, key);
114       return -1;
115     }
116 }
117
118 /* Parses and return O's value as a Boolean value.  "true" and "false", "yes"
119    and "no", "on" and "off", and "1" and "0" are acceptable boolean strings.
120
121    Destroys O. */
122 bool
123 parse_boolean (struct driver_option *o)
124 {
125   bool retval;
126
127   retval = do_parse_boolean (o->driver_name, o->name, o->default_value) > 0;
128   if (o->value != NULL)
129     {
130       int value = do_parse_boolean (o->driver_name, o->name, o->value);
131       if (value >= 0)
132         retval = value;
133     }
134
135   driver_option_destroy (o);
136
137   return retval;
138 }
139
140 /* Parses O's value as an enumeration constant.  The arguments to this function
141    consist of a series of string/int pairs, terminated by a null pointer value.
142    O's value is compared to each string in turn, and parse_enum() returns the
143    int associated with the first matching string.  If there is no match, or if
144    O has no user-specified value, then O's default value is treated the same
145    way.  If the default value still does not match, parse_enum() returns 0.
146
147    Example: parse_enum (o, "a", 1, "b", 2, NULL_SENTINEL) returns 1 if O's
148    value if "a", 2 if O's value is "b".
149
150    Destroys O. */
151 int
152 parse_enum (struct driver_option *o, ...)
153 {
154   va_list args;
155   int retval;
156
157   retval = 0;
158   va_start (args, o);
159   for (;;)
160     {
161       const char *s;
162       int value;
163
164       s = va_arg (args, const char *);
165       if (s == NULL)
166         {
167           if (o->value != NULL)
168             {
169               struct string choices;
170               int i;
171
172               ds_init_empty (&choices);
173               va_end (args);
174               va_start (args, o);
175               for (i = 0; ; i++)
176                 {
177                   s = va_arg (args, const char *);
178                   if (s == NULL)
179                     break;
180                   value = va_arg (args, int);
181
182                   if (i > 0)
183                     ds_put_cstr (&choices, ", ");
184                   ds_put_format (&choices, "`%s'", s);
185                 }
186
187               msg (MW, _("%s: `%s' is `%s' but one of the following "
188                              "is required: %s"),
189                      o->driver_name, o->name, o->value, ds_cstr (&choices));
190               ds_destroy (&choices);
191             }
192           break;
193         }
194       value = va_arg (args, int);
195
196       if (o->value != NULL && !strcmp (s, o->value))
197         {
198           retval = value;
199           break;
200         }
201       else if (!strcmp (s, o->default_value))
202         retval = value;
203     }
204   va_end (args);
205   driver_option_destroy (o);
206   return retval;
207 }
208
209 /* Parses O's value as an integer in the range MIN_VALUE to MAX_VALUE
210    (inclusive) and returns the integer.
211
212    Destroys O. */
213 int
214 parse_int (struct driver_option *o, int min_value, int max_value)
215 {
216   int retval = strtol (o->default_value, NULL, 0);
217
218   if (o->value != NULL)
219     {
220       int value;
221       char *tail;
222
223       errno = 0;
224       value = strtol (o->value, &tail, 0);
225       if (tail != o->value && *tail == '\0' && errno != ERANGE
226           && value >= min_value && value <= max_value)
227         retval = value;
228       else if (max_value == INT_MAX)
229         {
230           if (min_value == 0)
231             msg (MW, _("%s: `%s' is `%s' but a nonnegative integer "
232                            "is required"),
233                    o->driver_name, o->name, o->value);
234           else if (min_value == 1)
235             msg (MW, _("%s: `%s' is `%s' but a positive integer is "
236                            "required"), o->driver_name, o->name, o->value);
237           else if (min_value == INT_MIN)
238             msg (MW, _("%s: `%s' is `%s' but an integer is required"),
239                    o->driver_name, o->name, o->value);
240           else
241             msg (MW, _("%s: `%s' is `%s' but an integer greater "
242                            "than %d is required"),
243                    o->driver_name, o->name, o->value, min_value - 1);
244         }
245       else
246         msg (MW, _("%s: `%s' is `%s'  but an integer between %d and "
247                        "%d is required"),
248                o->driver_name, o->name, o->value, min_value, max_value);
249     }
250
251   driver_option_destroy (o);
252   return retval;
253 }
254
255 /* Parses O's value as a dimension, as understood by measure_dimension(), and
256    returns its length in units of 1/72000".
257
258    Destroys O. */
259 int
260 parse_dimension (struct driver_option *o)
261 {
262   int retval;
263
264   retval = o->value != NULL ? measure_dimension (o->value) : -1;
265   if (retval == -1)
266     retval = measure_dimension (o->default_value);
267
268   driver_option_destroy (o);
269   return retval;
270 }
271
272 /* Parses O's value as a string and returns it as a malloc'd string that the
273    caller is responsible for freeing.
274
275    Destroys O. */
276 char *
277 parse_string (struct driver_option *o)
278 {
279   char *retval = xstrdup (o->value != NULL ? o->value : o->default_value);
280   driver_option_destroy (o);
281   return retval;
282 }
283
284 static char *
285 default_chart_file_name (const char *file_name)
286 {
287   if (strcmp (file_name, "-"))
288     {
289       const char *extension = strrchr (file_name, '.');
290       int stem_length = extension ? extension - file_name : strlen (file_name);
291       return xasprintf ("%.*s-#.png", stem_length, file_name);
292     }
293   else
294     return NULL;
295 }
296
297 /* Parses and returns a chart file name, or NULL if no charts should be output.
298    If a nonnull string is returned, it will contain at least one '#' character,
299    which the client will presumably replace by a number as part of writing
300    charts to separate files.
301
302    If O->value is "none", then this function returns NULL.
303
304    If O->value is non-NULL but not "none", returns a copy of that string (if it
305    contains '#').
306
307    If O->value is NULL, then O's default_value should be the name of the main
308    output file.  Returns NULL if default_value is "-", and otherwise returns a
309    copy of string string with its extension stripped off and "-#.png" appended.
310
311    Destroys O. */
312 char *
313 parse_chart_file_name (struct driver_option *o)
314 {
315   char *chart_file_name;
316
317   if (o->value != NULL)
318     {
319       if (!strcmp (o->value, "none"))
320         chart_file_name = NULL;
321       else if (strchr (o->value, '#') != NULL)
322         chart_file_name = xstrdup (o->value);
323       else
324         {
325           msg (MW, _("%s: `%s' is `%s' but a file name that contains "
326                          "`#' is required."),
327                  o->name, o->value, o->driver_name);
328           chart_file_name = default_chart_file_name (o->default_value);
329         }
330     }
331   else
332     chart_file_name = default_chart_file_name (o->default_value);
333
334   driver_option_destroy (o);
335
336   return chart_file_name;
337 }