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