1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2010, 2014 Free Software Foundation, Inc.
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.
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.
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/>. */
19 #include "output/options.h"
28 #include "libpspp/hash-functions.h"
29 #include "libpspp/hmap.h"
30 #include "libpspp/str.h"
31 #include "libpspp/string-map.h"
32 #include "output/driver-provider.h"
33 #include "output/measure.h"
34 #include "output/table.h"
36 #include "gl/xalloc.h"
39 #define _(msgid) gettext (msgid)
41 /* Creates and returns a new struct driver_option that contains copies of
42 all of the supplied arguments. All of the arguments must be nonnull,
43 except that VALUE may be NULL (if the user did not supply a value for this
46 Refer to struct driver_option for the meaning of each argument. */
47 struct driver_option *
48 driver_option_create (const char *driver_name, const char *name,
49 const char *value, const char *default_value)
51 struct driver_option *o = xmalloc (sizeof *o);
52 o->driver_name = xstrdup (driver_name);
53 o->name = xstrdup (name);
54 o->value = xstrdup_if_nonnull (value);
55 o->default_value = xstrdup_if_nonnull (default_value);
59 /* Creates and returns a new struct driver_option for output driver DRIVER
60 (which is needed only to the extent that its name will be used in error
61 messages). The option named NAME is extracted from OPTIONS. DEFAULT_VALUE
62 is the default value of the option, used if the given option was not
63 supplied or was invalid. */
64 struct driver_option *
65 driver_option_get (struct output_driver *driver, struct string_map *options,
66 const char *name, const char *default_value)
68 struct driver_option *option;
71 value = string_map_find_and_delete (options, name);
72 option = driver_option_create (output_driver_get_name (driver), name, value,
78 /* Frees driver option O. */
80 driver_option_destroy (struct driver_option *o)
84 free (o->driver_name);
87 free (o->default_value);
92 /* Stores the paper size of the value of option O into *H and *V, in 1/72000"
93 units. Any syntax accepted by measure_paper() may be used.
97 parse_paper_size (struct driver_option *o, int *h, int *v)
99 if (o->value == NULL || !measure_paper (o->value, h, v))
100 measure_paper (o->default_value, h, v);
101 driver_option_destroy (o);
105 do_parse_boolean (const char *driver_name, const char *key,
108 if (!strcmp (value, "on") || !strcmp (value, "true")
109 || !strcmp (value, "yes") || !strcmp (value, "1"))
111 else if (!strcmp (value, "off") || !strcmp (value, "false")
112 || !strcmp (value, "no") || !strcmp (value, "0"))
116 msg (MW, _("%s: `%s' is `%s' but a Boolean value is required"),
117 driver_name, value, key);
122 /* Parses and return O's value as a Boolean value. "true" and "false", "yes"
123 and "no", "on" and "off", and "1" and "0" are acceptable boolean strings.
127 parse_boolean (struct driver_option *o)
131 retval = do_parse_boolean (o->driver_name, o->name, o->default_value) > 0;
132 if (o->value != NULL)
134 int value = do_parse_boolean (o->driver_name, o->name, o->value);
139 driver_option_destroy (o);
144 /* Parses O's value as an enumeration constant. The arguments to this function
145 consist of a series of string/int pairs, terminated by a null pointer value.
146 O's value is compared to each string in turn, and parse_enum() returns the
147 int associated with the first matching string. If there is no match, or if
148 O has no user-specified value, then O's default value is treated the same
149 way. If the default value still does not match, parse_enum() returns 0.
151 Example: parse_enum (o, "a", 1, "b", 2, NULL_SENTINEL) returns 1 if O's
152 value if "a", 2 if O's value is "b".
156 parse_enum (struct driver_option *o, ...)
168 s = va_arg (args, const char *);
171 if (o->value != NULL)
173 struct string choices;
176 ds_init_empty (&choices);
181 s = va_arg (args, const char *);
184 value = va_arg (args, int);
187 ds_put_cstr (&choices, ", ");
188 ds_put_format (&choices, "`%s'", s);
191 msg (MW, _("%s: `%s' is `%s' but one of the following "
193 o->driver_name, o->name, o->value, ds_cstr (&choices));
194 ds_destroy (&choices);
198 value = va_arg (args, int);
200 if (o->value != NULL && !strcmp (s, o->value))
205 else if (!strcmp (s, o->default_value))
209 driver_option_destroy (o);
213 /* Parses O's value as an integer in the range MIN_VALUE to MAX_VALUE
214 (inclusive) and returns the integer.
218 parse_int (struct driver_option *o, int min_value, int max_value)
220 int retval = strtol (o->default_value, NULL, 0);
222 if (o->value != NULL)
228 value = strtol (o->value, &tail, 0);
229 if (tail != o->value && *tail == '\0' && errno != ERANGE
230 && value >= min_value && value <= max_value)
232 else if (max_value == INT_MAX)
235 msg (MW, _("%s: `%s' is `%s' but a non-negative integer "
237 o->driver_name, o->name, o->value);
238 else if (min_value == 1)
239 msg (MW, _("%s: `%s' is `%s' but a positive integer is "
240 "required"), o->driver_name, o->name, o->value);
241 else if (min_value == INT_MIN)
242 msg (MW, _("%s: `%s' is `%s' but an integer is required"),
243 o->driver_name, o->name, o->value);
245 msg (MW, _("%s: `%s' is `%s' but an integer greater "
246 "than %d is required"),
247 o->driver_name, o->name, o->value, min_value - 1);
250 msg (MW, _("%s: `%s' is `%s' but an integer between %d and "
252 o->driver_name, o->name, o->value, min_value, max_value);
255 driver_option_destroy (o);
259 /* Parses O's value as a dimension, as understood by measure_dimension(), and
260 returns its length in units of 1/72000".
264 parse_dimension (struct driver_option *o)
268 retval = (o->value != NULL ? measure_dimension (o->value)
269 : o->default_value != NULL ? measure_dimension (o->default_value)
272 driver_option_destroy (o);
276 /* Parses O's value as a string and returns it as a malloc'd string that the
277 caller is responsible for freeing.
281 parse_string (struct driver_option *o)
283 char *retval = xstrdup (o->value != NULL ? o->value : o->default_value);
284 driver_option_destroy (o);
289 default_chart_file_name (const char *file_name)
291 if (strcmp (file_name, "-"))
293 const char *extension = strrchr (file_name, '.');
294 int stem_length = extension ? extension - file_name : strlen (file_name);
295 return xasprintf ("%.*s-#", stem_length, file_name);
301 /* Parses and returns a chart file name, or NULL if no charts should be output.
302 If a nonnull string is returned, it will contain at least one '#' character,
303 which the client will presumably replace by a number as part of writing
304 charts to separate files.
306 If O->value is "none", then this function returns NULL.
308 If O->value is non-NULL but not "none", returns a copy of that string (if it
311 If O->value is NULL, then O's default_value should be the name of the main
312 output file. Returns NULL if default_value is "-", and otherwise returns a
313 copy of string string with its extension stripped off and "-#.png" appended.
317 parse_chart_file_name (struct driver_option *o)
319 char *chart_file_name;
321 if (o->value != NULL)
323 if (!strcmp (o->value, "none"))
324 chart_file_name = NULL;
325 else if (strchr (o->value, '#') != NULL)
326 chart_file_name = xstrdup (o->value);
329 msg (MW, _("%s: `%s' is `%s' but a file name that contains "
331 o->driver_name, o->name, o->value);
332 chart_file_name = default_chart_file_name (o->default_value);
336 chart_file_name = default_chart_file_name (o->default_value);
338 driver_option_destroy (o);
340 return chart_file_name;
344 lookup_color_name (const char *s)
348 struct hmap_node hmap_node;
353 static struct color colors[] =
355 { .name = "aliceblue", .code = 0xf0f8ff },
356 { .name = "antiquewhite", .code = 0xfaebd7 },
357 { .name = "aqua", .code = 0x00ffff },
358 { .name = "aquamarine", .code = 0x7fffd4 },
359 { .name = "azure", .code = 0xf0ffff },
360 { .name = "beige", .code = 0xf5f5dc },
361 { .name = "bisque", .code = 0xffe4c4 },
362 { .name = "black", .code = 0x000000 },
363 { .name = "blanchedalmond", .code = 0xffebcd },
364 { .name = "blue", .code = 0x0000ff },
365 { .name = "blueviolet", .code = 0x8a2be2 },
366 { .name = "brown", .code = 0xa52a2a },
367 { .name = "burlywood", .code = 0xdeb887 },
368 { .name = "cadetblue", .code = 0x5f9ea0 },
369 { .name = "chartreuse", .code = 0x7fff00 },
370 { .name = "chocolate", .code = 0xd2691e },
371 { .name = "coral", .code = 0xff7f50 },
372 { .name = "cornflowerblue", .code = 0x6495ed },
373 { .name = "cornsilk", .code = 0xfff8dc },
374 { .name = "crimson", .code = 0xdc143c },
375 { .name = "cyan", .code = 0x00ffff },
376 { .name = "darkblue", .code = 0x00008b },
377 { .name = "darkcyan", .code = 0x008b8b },
378 { .name = "darkgoldenrod", .code = 0xb8860b },
379 { .name = "darkgray", .code = 0xa9a9a9 },
380 { .name = "darkgreen", .code = 0x006400 },
381 { .name = "darkgrey", .code = 0xa9a9a9 },
382 { .name = "darkkhaki", .code = 0xbdb76b },
383 { .name = "darkmagenta", .code = 0x8b008b },
384 { .name = "darkolivegreen", .code = 0x556b2f },
385 { .name = "darkorange", .code = 0xff8c00 },
386 { .name = "darkorchid", .code = 0x9932cc },
387 { .name = "darkred", .code = 0x8b0000 },
388 { .name = "darksalmon", .code = 0xe9967a },
389 { .name = "darkseagreen", .code = 0x8fbc8f },
390 { .name = "darkslateblue", .code = 0x483d8b },
391 { .name = "darkslategray", .code = 0x2f4f4f },
392 { .name = "darkslategrey", .code = 0x2f4f4f },
393 { .name = "darkturquoise", .code = 0x00ced1 },
394 { .name = "darkviolet", .code = 0x9400d3 },
395 { .name = "deeppink", .code = 0xff1493 },
396 { .name = "deepskyblue", .code = 0x00bfff },
397 { .name = "dimgray", .code = 0x696969 },
398 { .name = "dimgrey", .code = 0x696969 },
399 { .name = "dodgerblue", .code = 0x1e90ff },
400 { .name = "firebrick", .code = 0xb22222 },
401 { .name = "floralwhite", .code = 0xfffaf0 },
402 { .name = "forestgreen", .code = 0x228b22 },
403 { .name = "fuchsia", .code = 0xff00ff },
404 { .name = "gainsboro", .code = 0xdcdcdc },
405 { .name = "ghostwhite", .code = 0xf8f8ff },
406 { .name = "gold", .code = 0xffd700 },
407 { .name = "goldenrod", .code = 0xdaa520 },
408 { .name = "gray", .code = 0x808080 },
409 { .name = "green", .code = 0x008000 },
410 { .name = "greenyellow", .code = 0xadff2f },
411 { .name = "grey", .code = 0x808080 },
412 { .name = "honeydew", .code = 0xf0fff0 },
413 { .name = "hotpink", .code = 0xff69b4 },
414 { .name = "indianred", .code = 0xcd5c5c },
415 { .name = "indigo", .code = 0x4b0082 },
416 { .name = "ivory", .code = 0xfffff0 },
417 { .name = "khaki", .code = 0xf0e68c },
418 { .name = "lavender", .code = 0xe6e6fa },
419 { .name = "lavenderblush", .code = 0xfff0f5 },
420 { .name = "lawngreen", .code = 0x7cfc00 },
421 { .name = "lemonchiffon", .code = 0xfffacd },
422 { .name = "lightblue", .code = 0xadd8e6 },
423 { .name = "lightcoral", .code = 0xf08080 },
424 { .name = "lightcyan", .code = 0xe0ffff },
425 { .name = "lightgoldenrodyellow", .code = 0xfafad2 },
426 { .name = "lightgray", .code = 0xd3d3d3 },
427 { .name = "lightgreen", .code = 0x90ee90 },
428 { .name = "lightgrey", .code = 0xd3d3d3 },
429 { .name = "lightpink", .code = 0xffb6c1 },
430 { .name = "lightsalmon", .code = 0xffa07a },
431 { .name = "lightseagreen", .code = 0x20b2aa },
432 { .name = "lightskyblue", .code = 0x87cefa },
433 { .name = "lightslategray", .code = 0x778899 },
434 { .name = "lightslategrey", .code = 0x778899 },
435 { .name = "lightsteelblue", .code = 0xb0c4de },
436 { .name = "lightyellow", .code = 0xffffe0 },
437 { .name = "lime", .code = 0x00ff00 },
438 { .name = "limegreen", .code = 0x32cd32 },
439 { .name = "linen", .code = 0xfaf0e6 },
440 { .name = "magenta", .code = 0xff00ff },
441 { .name = "maroon", .code = 0x800000 },
442 { .name = "mediumaquamarine", .code = 0x66cdaa },
443 { .name = "mediumblue", .code = 0x0000cd },
444 { .name = "mediumorchid", .code = 0xba55d3 },
445 { .name = "mediumpurple", .code = 0x9370db },
446 { .name = "mediumseagreen", .code = 0x3cb371 },
447 { .name = "mediumslateblue", .code = 0x7b68ee },
448 { .name = "mediumspringgreen", .code = 0x00fa9a },
449 { .name = "mediumturquoise", .code = 0x48d1cc },
450 { .name = "mediumvioletred", .code = 0xc71585 },
451 { .name = "midnightblue", .code = 0x191970 },
452 { .name = "mintcream", .code = 0xf5fffa },
453 { .name = "mistyrose", .code = 0xffe4e1 },
454 { .name = "moccasin", .code = 0xffe4b5 },
455 { .name = "navajowhite", .code = 0xffdead },
456 { .name = "navy", .code = 0x000080 },
457 { .name = "oldlace", .code = 0xfdf5e6 },
458 { .name = "olive", .code = 0x808000 },
459 { .name = "olivedrab", .code = 0x6b8e23 },
460 { .name = "orange", .code = 0xffa500 },
461 { .name = "orangered", .code = 0xff4500 },
462 { .name = "orchid", .code = 0xda70d6 },
463 { .name = "palegoldenrod", .code = 0xeee8aa },
464 { .name = "palegreen", .code = 0x98fb98 },
465 { .name = "paleturquoise", .code = 0xafeeee },
466 { .name = "palevioletred", .code = 0xdb7093 },
467 { .name = "papayawhip", .code = 0xffefd5 },
468 { .name = "peachpuff", .code = 0xffdab9 },
469 { .name = "peru", .code = 0xcd853f },
470 { .name = "pink", .code = 0xffc0cb },
471 { .name = "plum", .code = 0xdda0dd },
472 { .name = "powderblue", .code = 0xb0e0e6 },
473 { .name = "purple", .code = 0x800080 },
474 { .name = "red", .code = 0xff0000 },
475 { .name = "rosybrown", .code = 0xbc8f8f },
476 { .name = "royalblue", .code = 0x4169e1 },
477 { .name = "saddlebrown", .code = 0x8b4513 },
478 { .name = "salmon", .code = 0xfa8072 },
479 { .name = "sandybrown", .code = 0xf4a460 },
480 { .name = "seagreen", .code = 0x2e8b57 },
481 { .name = "seashell", .code = 0xfff5ee },
482 { .name = "sienna", .code = 0xa0522d },
483 { .name = "silver", .code = 0xc0c0c0 },
484 { .name = "skyblue", .code = 0x87ceeb },
485 { .name = "slateblue", .code = 0x6a5acd },
486 { .name = "slategray", .code = 0x708090 },
487 { .name = "slategrey", .code = 0x708090 },
488 { .name = "snow", .code = 0xfffafa },
489 { .name = "springgreen", .code = 0x00ff7f },
490 { .name = "steelblue", .code = 0x4682b4 },
491 { .name = "tan", .code = 0xd2b48c },
492 { .name = "teal", .code = 0x008080 },
493 { .name = "thistle", .code = 0xd8bfd8 },
494 { .name = "tomato", .code = 0xff6347 },
495 { .name = "turquoise", .code = 0x40e0d0 },
496 { .name = "violet", .code = 0xee82ee },
497 { .name = "wheat", .code = 0xf5deb3 },
498 { .name = "white", .code = 0xffffff },
499 { .name = "whitesmoke", .code = 0xf5f5f5 },
500 { .name = "yellow", .code = 0xffff00 },
501 { .name = "yellowgreen", .code = 0x9acd32 },
504 static struct hmap color_table = HMAP_INITIALIZER (color_table);
506 if (hmap_is_empty (&color_table))
507 for (size_t i = 0; i < sizeof colors / sizeof *colors; i++)
508 hmap_insert (&color_table, &colors[i].hmap_node,
509 hash_string (colors[i].name, 0));
511 const struct color *color;
512 HMAP_FOR_EACH_WITH_HASH (color, struct color, hmap_node,
513 hash_string (s, 0), &color_table)
514 if (!strcmp (color->name, s))
520 parse_color__ (const char *s, struct cell_color *color)
523 uint16_t r16, g16, b16;
525 if (sscanf (s, "#%4"SCNx16"%4"SCNx16"%4"SCNx16"%n",
526 &r16, &g16, &b16, &len) == 3
539 if (sscanf (s, "#%2"SCNx8"%2"SCNx8"%2"SCNx8"%n", &r, &g, &b, &len) == 3
551 if (sscanf (s, "%2"SCNx8"%2"SCNx8"%2"SCNx8"%n", &r, &g, &b, &len) == 3
563 if (sscanf (s, "rgb (%"SCNi8" , %"SCNi8" , %"SCNi8") %n",
564 &r, &g, &b, &len) == 3
574 /* rgba(r,g,b,a), ignoring a. */
576 if (sscanf (s, "rgba (%"SCNi8" , %"SCNi8" , %"SCNi8", %lf) %n",
577 &r, &g, &b, &alpha, &len) == 4
583 color->alpha = alpha <= 0 ? 0 : alpha >= 1 ? 255 : alpha * 255.0;
587 int code = lookup_color_name (s);
590 color->r = code >> 16;
591 color->g = code >> 8;
597 if (!strcmp (s, "transparent"))
599 *color = (struct cell_color) { .alpha = 0 };
606 /* Parses and returns color information from O. */
608 parse_color (struct driver_option *o)
610 struct cell_color color = CELL_COLOR_BLACK;
611 parse_color__ (o->default_value, &color);
614 if (!parse_color__ (o->value, &color))
615 msg (MW, _("%s: `%s' is `%s', which could not be parsed as a color"),
616 o->driver_name, o->name, o->value);
618 driver_option_destroy (o);