From: Ben Pfaff Date: Wed, 8 Feb 2023 19:21:10 +0000 (-0800) Subject: output: Refactor driver options to avoid so much copying. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bfafd2fc47742719938ce1219f445b3ad25fb703;p=pspp output: Refactor driver options to avoid so much copying. --- diff --git a/src/output/ascii.c b/src/output/ascii.c index b94d4941c2..692f7f4d42 100644 --- a/src/output/ascii.c +++ b/src/output/ascii.c @@ -326,7 +326,7 @@ static void ascii_submit (struct output_driver *, const struct output_item *); static int get_terminal_width (void); static bool update_page_size (struct ascii_driver *, bool issue_error); -static int parse_page_size (struct driver_option *); +static int parse_page_size (struct driver_option); static void ascii_draw_line (void *, int bb[TABLE_N_AXES][2], const struct table_border_style[TABLE_N_AXES][2]); @@ -346,10 +346,10 @@ ascii_driver_cast (struct output_driver *driver) return UP_CAST (driver, struct ascii_driver, driver); } -static struct driver_option * -opt (struct string_map *options, const char *key, const char *default_value) +static struct driver_option +opt (struct driver_options *options, const char *key, const char *default_value) { - return driver_option_get ("ascii", options, key, default_value); + return driver_option_get (options, key, default_value); } /* Return true iff the terminal appears to be an xterm with @@ -366,8 +366,8 @@ term_is_utf8_xterm (void) } static struct output_driver * -ascii_create (struct file_handle *fh, enum settings_output_devices device_type, - struct string_map *o) +ascii_create (struct file_handle *fh, enum settings_output_devices device_type, + struct driver_options *o) { bool append = parse_boolean (opt (o, "append", "false")); FILE *file = fn_open (fh, append ? "a" : "w"); @@ -455,13 +455,13 @@ error: } static int -parse_page_size (struct driver_option *option) +parse_page_size (struct driver_option option) { - int dim = atol (option->default_value); + int dim = atol (option.default_value); - if (option->value != NULL) + if (option.value != NULL) { - if (!strcmp (option->value, "auto")) + if (!strcmp (option.value, "auto")) dim = -1; else { @@ -469,17 +469,15 @@ parse_page_size (struct driver_option *option) char *tail; errno = 0; - value = strtol (option->value, &tail, 0); + value = strtol (option.value, &tail, 0); if (value >= 1 && errno != ERANGE && *tail == '\0') dim = value; else msg (MW, _("%s: %s must be positive integer or `auto'"), - option->driver_name, option->name); + option.driver_name, option.name); } } - driver_option_destroy (option); - return dim; } diff --git a/src/output/cairo.c b/src/output/cairo.c index da224346f8..8ef2b09536 100644 --- a/src/output/cairo.c +++ b/src/output/cairo.c @@ -129,10 +129,10 @@ xr_driver_cast (struct output_driver *driver) return UP_CAST (driver, struct xr_driver, driver); } -static struct driver_option * -opt (struct string_map *options, const char *key, const char *default_value) +static struct driver_option +opt (struct driver_options *options, const char *key, const char *default_value) { - return driver_option_get ("cairo", options, key, default_value); + return driver_option_get (options, key, default_value); } static PangoFontDescription * @@ -162,7 +162,7 @@ parse_font (const char *font, int default_size, bool bold, bool italic) } static PangoFontDescription * -parse_font_option (struct string_map *options, +parse_font_option (struct driver_options *options, const char *key, const char *default_value, int default_size, bool bold, bool italic) { @@ -184,7 +184,7 @@ parse_font_option (struct string_map *options, static struct xr_driver * xr_allocate (const char *name, int device_type, - enum xr_output_type output_type, struct string_map *o) + enum xr_output_type output_type, struct driver_options *o) { /* Scale factor from inch/72000 to inch/(72 * XR_POINT). */ const double scale = XR_POINT / 1000.; @@ -281,7 +281,7 @@ xr_allocate (const char *name, int device_type, static struct output_driver * xr_create (struct file_handle *fh, enum settings_output_devices device_type, - struct string_map *o, enum xr_output_type output_type) + struct driver_options *o, enum xr_output_type output_type) { const char *file_name = fh_get_file_name (fh); struct xr_driver *xr = xr_allocate (file_name, device_type, output_type, o); @@ -328,29 +328,29 @@ xr_create (struct file_handle *fh, enum settings_output_devices device_type, } static struct output_driver * -xr_pdf_create (struct file_handle *fh, enum settings_output_devices device_type, - struct string_map *o) +xr_pdf_create (struct file_handle *fh, enum settings_output_devices device_type, + struct driver_options *o) { return xr_create (fh, device_type, o, XR_PDF); } static struct output_driver * -xr_ps_create (struct file_handle *fh, enum settings_output_devices device_type, - struct string_map *o) +xr_ps_create (struct file_handle *fh, enum settings_output_devices device_type, + struct driver_options *o) { return xr_create (fh, device_type, o, XR_PS); } static struct output_driver * xr_svg_create (struct file_handle *fh, enum settings_output_devices device_type, - struct string_map *o) + struct driver_options *o) { return xr_create (fh, device_type, o, XR_SVG); } static struct output_driver * xr_png_create (struct file_handle *fh, enum settings_output_devices device_type, - struct string_map *o) + struct driver_options *o) { return xr_create (fh, device_type, o, XR_PNG); } diff --git a/src/output/csv.c b/src/output/csv.c index e63e828d4e..e6b769b383 100644 --- a/src/output/csv.c +++ b/src/output/csv.c @@ -65,15 +65,15 @@ csv_driver_cast (struct output_driver *driver) return UP_CAST (driver, struct csv_driver, driver); } -static struct driver_option * -opt (struct string_map *options, const char *key, const char *default_value) +static struct driver_option +opt (struct driver_options *options, const char *key, const char *default_value) { - return driver_option_get ("csv", options, key, default_value); + return driver_option_get (options, key, default_value); } static struct output_driver * csv_create (struct file_handle *fh, enum settings_output_devices device_type, - struct string_map *o) + struct driver_options *o) { FILE *file = fn_open (fh, "w"); if (!file) diff --git a/src/output/driver-provider.h b/src/output/driver-provider.h index d0a054e8e0..ffa483b461 100644 --- a/src/output/driver-provider.h +++ b/src/output/driver-provider.h @@ -28,6 +28,7 @@ struct output_iterator; struct string_map; struct file_handle; struct page_setup; +struct driver_options; /* A configured output driver. */ struct output_driver @@ -113,7 +114,7 @@ struct output_driver_factory is desirable). */ struct output_driver *(*create) (struct file_handle *, enum settings_output_devices type, - struct string_map *options); + struct driver_options *); }; #endif /* output/driver-provider.h */ diff --git a/src/output/driver.c b/src/output/driver.c index 92de2f5f17..ba2d54f9f5 100644 --- a/src/output/driver.c +++ b/src/output/driver.c @@ -18,6 +18,7 @@ #include "output/driver.h" #include "output/driver-provider.h" +#include "output/options.h" #include #include @@ -538,19 +539,11 @@ default_device_type (const char *file_name) struct output_driver * output_driver_create (struct string_map *options) { - enum settings_output_devices device_type; - const struct output_driver_factory *f; - struct output_driver *driver; - char *device_string; - char *file_name; - char *format; - - format = string_map_find_and_delete (options, "format"); - file_name = string_map_find_and_delete (options, "output-file"); - - if (format == NULL) + char *format = string_map_find_and_delete (options, "format"); + char *file_name = string_map_find_and_delete (options, "output-file"); + if (!format) { - if (file_name != NULL) + if (file_name) { const char *extension = strrchr (file_name, '.'); format = xstrdup (extension != NULL ? extension + 1 : ""); @@ -558,42 +551,45 @@ output_driver_create (struct string_map *options) else format = xstrdup ("txt"); } - f = find_factory (format); + const struct output_driver_factory *f = find_factory (format); + + struct driver_options o = { + .driver_name = f->extension, + .map = STRING_MAP_INITIALIZER (o.map), + .garbage = STRING_ARRAY_INITIALIZER, + }; + string_map_swap (&o.map, options); if (file_name == NULL) file_name = xstrdup (f->default_file_name); /* XXX should use parse_enum(). */ - device_string = string_map_find_and_delete (options, "device"); - if (device_string == NULL || device_string[0] == '\0') - device_type = default_device_type (file_name); - else if (!strcmp (device_string, "terminal")) - device_type = SETTINGS_DEVICE_TERMINAL; - else if (!strcmp (device_string, "listing")) - device_type = SETTINGS_DEVICE_LISTING; - else - { - msg (MW, _("%s is not a valid device type (the choices are `%s' and `%s')"), - device_string, "terminal", "listing"); - device_type = default_device_type (file_name); - } - - struct file_handle *fh = fh_create_file (NULL, file_name, NULL, fh_default_properties ()); - - driver = f->create (fh, device_type, options); - if (driver != NULL) + enum settings_output_devices default_type = default_device_type (file_name); + const char *default_type_string = (default_type == SETTINGS_DEVICE_TERMINAL + ? "terminal" : "listing"); + enum settings_output_devices device_type = parse_enum ( + driver_option_get (&o, "device", default_type_string), + "terminal", SETTINGS_DEVICE_TERMINAL, + "listing", SETTINGS_DEVICE_LISTING, + NULL_SENTINEL); + + struct file_handle *fh = fh_create_file (NULL, file_name, NULL, + fh_default_properties ()); + struct output_driver *driver = f->create (fh, device_type, &o); + if (driver) { const struct string_map_node *node; const char *key; - STRING_MAP_FOR_EACH_KEY (key, node, options) + STRING_MAP_FOR_EACH_KEY (key, node, &o.map) msg (MW, _("%s: unknown option `%s'"), file_name, key); } - string_map_clear (options); + + string_map_destroy (&o.map); + string_array_destroy (&o.garbage); free (file_name); free (format); - free (device_string); return driver; } diff --git a/src/output/html.c b/src/output/html.c index 0e6e8d822d..d3049e6564 100644 --- a/src/output/html.c +++ b/src/output/html.c @@ -84,10 +84,10 @@ html_driver_cast (struct output_driver *driver) return UP_CAST (driver, struct html_driver, driver); } -static struct driver_option * -opt (struct string_map *options, const char *key, const char *default_value) +static struct driver_option +opt (struct driver_options *options, const char *key, const char *default_value) { - return driver_option_get ("html", options, key, default_value); + return driver_option_get (options, key, default_value); } static void @@ -176,7 +176,7 @@ put_header (struct html_driver *html) static struct output_driver * html_create (struct file_handle *fh, enum settings_output_devices device_type, - struct string_map *o) + struct driver_options *o) { FILE *file = fn_open (fh, "w"); if (!file) diff --git a/src/output/odt.c b/src/output/odt.c index 2f58586530..b0cf5cef9e 100644 --- a/src/output/odt.c +++ b/src/output/odt.c @@ -282,7 +282,7 @@ write_meta_data (struct odt_driver *odt) static struct output_driver * odt_create (struct file_handle *fh, enum settings_output_devices device_type, - struct string_map *o UNUSED) + struct driver_options *o UNUSED) { const char *file_name = fh_get_file_name (fh); struct zip_writer *zip = zip_writer_create (file_name); diff --git a/src/output/options.c b/src/output/options.c index 23715c6932..c05ea746c0 100644 --- a/src/output/options.c +++ b/src/output/options.c @@ -42,42 +42,28 @@ is used only in error messages). The option named NAME is extracted from OPTIONS. DEFAULT_VALUE is the default value of the option, used if the given option was not supplied or was invalid. */ -struct driver_option * -driver_option_get (const char *driver_name, struct string_map *options, +struct driver_option +driver_option_get (struct driver_options *options, const char *name, const char *default_value) { - struct driver_option *option = xmalloc (sizeof *option); - option->driver_name = xstrdup (driver_name); - option->name = xstrdup (name); - option->value = string_map_find_and_delete (options, name); - option->default_value = xstrdup_if_nonnull (default_value); - return option; -} - -/* Frees driver option O. */ -void -driver_option_destroy (struct driver_option *o) -{ - if (o != NULL) - { - free (o->driver_name); - free (o->name); - free (o->value); - free (o->default_value); - free (o); - } + char *value = string_map_find_and_delete (&options->map, name); + if (value) + string_array_append_nocopy (&options->garbage, value); + return (struct driver_option) { + .driver_name = options->driver_name, + .name = name, + .value = value, + .default_value = default_value, + }; } /* Stores the paper size of the value of option O into *H and *V, in 1/72000" - units. Any syntax accepted by measure_paper() may be used. - - Destroys O. */ + units. Any syntax accepted by measure_paper() may be used. */ void -parse_paper_size (struct driver_option *o, int *h, int *v) +parse_paper_size (struct driver_option o, int *h, int *v) { - if (o->value == NULL || !measure_paper (o->value, h, v)) - measure_paper (o->default_value, h, v); - driver_option_destroy (o); + if (!o.value || !measure_paper (o.value, h, v)) + measure_paper (o.default_value, h, v); } static int @@ -99,24 +85,18 @@ do_parse_boolean (const char *driver_name, const char *key, } /* Parses and return O's value as a Boolean value. "true" and "false", "yes" - and "no", "on" and "off", and "1" and "0" are acceptable boolean strings. - - Destroys O. */ + and "no", "on" and "off", and "1" and "0" are acceptable boolean strings. */ bool -parse_boolean (struct driver_option *o) +parse_boolean (struct driver_option o) { - bool retval; - - retval = do_parse_boolean (o->driver_name, o->name, o->default_value) > 0; - if (o->value != NULL) + bool retval = do_parse_boolean (o.driver_name, o.name, o.default_value) > 0; + if (o.value) { - int value = do_parse_boolean (o->driver_name, o->name, o->value); + int value = do_parse_boolean (o.driver_name, o.name, o.value); if (value >= 0) retval = value; } - driver_option_destroy (o); - return retval; } @@ -128,39 +108,30 @@ parse_boolean (struct driver_option *o) way. If the default value still does not match, parse_enum() returns 0. Example: parse_enum (o, "a", 1, "b", 2, NULL_SENTINEL) returns 1 if O's - value if "a", 2 if O's value is "b". - - Destroys O. */ + value if "a", 2 if O's value is "b". */ int -parse_enum (struct driver_option *o, ...) +parse_enum (struct driver_option o, ...) { va_list args; - int retval; - - retval = 0; va_start (args, o); + + int retval = 0; for (;;) { - const char *s; - int value; - - s = va_arg (args, const char *); - if (s == NULL) + const char *s = va_arg (args, const char *); + if (!s) { - if (o->value != NULL) + if (o.value) { - struct string choices; - int i; - - ds_init_empty (&choices); + struct string choices = DS_EMPTY_INITIALIZER; va_end (args); va_start (args, o); - for (i = 0; ; i++) + for (int i = 0; ; i++) { s = va_arg (args, const char *); - if (s == NULL) + if (!s) break; - value = va_arg (args, int); + va_arg (args, int); if (i > 0) ds_put_cstr (&choices, ", "); @@ -169,43 +140,39 @@ parse_enum (struct driver_option *o, ...) msg (MW, _("%s: `%s' is `%s' but one of the following " "is required: %s"), - o->driver_name, o->name, o->value, ds_cstr (&choices)); + o.driver_name, o.name, o.value, ds_cstr (&choices)); ds_destroy (&choices); } break; } - value = va_arg (args, int); - if (o->value != NULL && !strcmp (s, o->value)) + int value = va_arg (args, int); + if (o.value && !strcmp (s, o.value)) { retval = value; break; } - else if (!strcmp (s, o->default_value)) + else if (!strcmp (s, o.default_value)) retval = value; } va_end (args); - driver_option_destroy (o); return retval; } /* Parses O's value as an integer in the range MIN_VALUE to MAX_VALUE - (inclusive) and returns the integer. - - Destroys O. */ + (inclusive) and returns the integer. */ int -parse_int (struct driver_option *o, int min_value, int max_value) +parse_int (struct driver_option o, int min_value, int max_value) { - int retval = strtol (o->default_value, NULL, 0); + int retval = strtol (o.default_value, NULL, 0); - if (o->value != NULL) + if (o.value) { - int value; - char *tail; - errno = 0; - value = strtol (o->value, &tail, 0); - if (tail != o->value && *tail == '\0' && errno != ERANGE + + char *tail; + int value = strtol (o.value, &tail, 0); + if (tail != o.value && *tail == '\0' && errno != ERANGE && value >= min_value && value <= max_value) retval = value; else if (max_value == INT_MAX) @@ -213,68 +180,53 @@ parse_int (struct driver_option *o, int min_value, int max_value) if (min_value == 0) msg (MW, _("%s: `%s' is `%s' but a non-negative integer " "is required"), - o->driver_name, o->name, o->value); + o.driver_name, o.name, o.value); else if (min_value == 1) msg (MW, _("%s: `%s' is `%s' but a positive integer is " - "required"), o->driver_name, o->name, o->value); + "required"), o.driver_name, o.name, o.value); else if (min_value == INT_MIN) msg (MW, _("%s: `%s' is `%s' but an integer is required"), - o->driver_name, o->name, o->value); + o.driver_name, o.name, o.value); else msg (MW, _("%s: `%s' is `%s' but an integer greater " "than %d is required"), - o->driver_name, o->name, o->value, min_value - 1); + o.driver_name, o.name, o.value, min_value - 1); } else msg (MW, _("%s: `%s' is `%s' but an integer between %d and " "%d is required"), - o->driver_name, o->name, o->value, min_value, max_value); + o.driver_name, o.name, o.value, min_value, max_value); } - - driver_option_destroy (o); return retval; } /* Parses O's value as a dimension, as understood by measure_dimension(), and - returns its length in units of 1/72000". - - Destroys O. */ + returns its length in units of 1/72000". */ int -parse_dimension (struct driver_option *o) +parse_dimension (struct driver_option o) { - int retval; - - retval = (o->value != NULL ? measure_dimension (o->value) - : o->default_value != NULL ? measure_dimension (o->default_value) - : -1); - - driver_option_destroy (o); - return retval; + return (o.value ? measure_dimension (o.value) + : o.default_value ? measure_dimension (o.default_value) + : -1); } /* Parses O's value as a string and returns it as a malloc'd string that the - caller is responsible for freeing. - - Destroys O. */ + caller is responsible for freeing. */ char * -parse_string (struct driver_option *o) +parse_string (struct driver_option o) { - char *retval = xstrdup (o->value != NULL ? o->value : o->default_value); - driver_option_destroy (o); - return retval; + return xstrdup (o.value ? o.value : o.default_value); } static char * default_chart_file_name (const char *file_name) { - if (strcmp (file_name, "-")) - { - const char *extension = strrchr (file_name, '.'); - int stem_length = extension ? extension - file_name : strlen (file_name); - return xasprintf ("%.*s-#", stem_length, file_name); - } - else + if (!strcmp (file_name, "-")) return NULL; + + const char *extension = strrchr (file_name, '.'); + int stem_length = extension ? extension - file_name : strlen (file_name); + return xasprintf ("%.*s-#", stem_length, file_name); } /* Parses and returns a chart file name, or NULL if no charts should be output. @@ -282,41 +234,31 @@ default_chart_file_name (const char *file_name) which the client will presumably replace by a number as part of writing charts to separate files. - If O->value is "none", then this function returns NULL. + If o.value is "none", then this function returns NULL. - If O->value is non-NULL but not "none", returns a copy of that string (if it + If o.value is non-NULL but not "none", returns a copy of that string (if it contains '#'). - If O->value is NULL, then O's default_value should be the name of the main + If o.value is NULL, then O's default_value should be the name of the main output file. Returns NULL if default_value is "-", and otherwise returns a - copy of string string with its extension stripped off and "-#.png" appended. - - Destroys O. */ + copy of string string with its extension stripped off and "-#.png" + appended. */ char * -parse_chart_file_name (struct driver_option *o) +parse_chart_file_name (struct driver_option o) { - char *chart_file_name; - - if (o->value != NULL) + if (!o.value) + return default_chart_file_name (o.default_value); + else if (!strcmp (o.value, "none")) + return NULL; + else if (strchr (o.value, '#') != NULL) + return xstrdup (o.value); + else { - if (!strcmp (o->value, "none")) - chart_file_name = NULL; - else if (strchr (o->value, '#') != NULL) - chart_file_name = xstrdup (o->value); - else - { - msg (MW, _("%s: `%s' is `%s' but a file name that contains " - "`#' is required."), - o->driver_name, o->name, o->value); - chart_file_name = default_chart_file_name (o->default_value); - } + msg (MW, _("%s: `%s' is `%s' but a file name that contains " + "`#' is required."), + o.driver_name, o.name, o.value); + return default_chart_file_name (o.default_value); } - else - chart_file_name = default_chart_file_name (o->default_value); - - driver_option_destroy (o); - - return chart_file_name; } static int @@ -584,17 +526,13 @@ parse_color__ (const char *s, struct cell_color *color) /* Parses and returns color information from O. */ struct cell_color -parse_color (struct driver_option *o) +parse_color (struct driver_option o) { struct cell_color color = CELL_COLOR_BLACK; - parse_color__ (o->default_value, &color); - if (o->value) - { - if (!parse_color__ (o->value, &color)) - msg (MW, _("%s: `%s' is `%s', which could not be parsed as a color"), - o->driver_name, o->name, o->value); - } - driver_option_destroy (o); + parse_color__ (o.default_value, &color); + if (o.value && !parse_color__ (o.value, &color)) + msg (MW, _("%s: `%s' is `%s', which could not be parsed as a color"), + o.driver_name, o.name, o.value); return color; } diff --git a/src/output/options.h b/src/output/options.h index 36d7bac9fb..2c0fc5c13f 100644 --- a/src/output/options.h +++ b/src/output/options.h @@ -21,34 +21,41 @@ #include #include "libpspp/compiler.h" +#include "libpspp/string-map.h" +#include "libpspp/string-array.h" struct output_driver; struct string_map; +struct driver_options + { + const char *driver_name; + struct string_map map; + struct string_array garbage; + }; + /* An option being parsed. */ struct driver_option { - char *driver_name; /* Driver's name, for use in error messages. */ - char *name; /* Option name, for use in error messages. */ - char *value; /* Value supplied by user (NULL if none). */ - char *default_value; /* Default value supplied by driver. */ + const char *driver_name; /* Driver's name, for use in error messages. */ + const char *name; /* Option name, for use in error messages. */ + const char *value; /* Value supplied by user (NULL if none). */ + const char *default_value; /* Default value supplied by driver. */ }; -struct driver_option *driver_option_get (const char *driver_name, - struct string_map *, - const char *name, - const char *default_value); -void driver_option_destroy (struct driver_option *); - -void parse_paper_size (struct driver_option *, int *h, int *v); -bool parse_boolean (struct driver_option *); -int parse_enum (struct driver_option *, ...) SENTINEL(0); -int parse_int (struct driver_option *, int min_value, int max_value); -int parse_dimension (struct driver_option *); -char *parse_string (struct driver_option *); -char *parse_chart_file_name (struct driver_option *); - -struct cell_color parse_color (struct driver_option *); +struct driver_option driver_option_get (struct driver_options *, + const char *name, + const char *default_value); + +void parse_paper_size (struct driver_option, int *h, int *v); +bool parse_boolean (struct driver_option); +int parse_enum (struct driver_option, ...) SENTINEL(0); +int parse_int (struct driver_option, int min_value, int max_value); +int parse_dimension (struct driver_option); +char *parse_string (struct driver_option); +char *parse_chart_file_name (struct driver_option); + +struct cell_color parse_color (struct driver_option); bool parse_color__ (const char *, struct cell_color *); #endif /* output/options.h */ diff --git a/src/output/spv-driver.c b/src/output/spv-driver.c index 5dcb9fee21..6fdcbb5bdc 100644 --- a/src/output/spv-driver.c +++ b/src/output/spv-driver.c @@ -49,7 +49,7 @@ spv_driver_cast (struct output_driver *driver) static struct output_driver * spv_create (struct file_handle *fh, enum settings_output_devices device_type, - struct string_map *o UNUSED) + struct driver_options *o UNUSED) { struct spv_writer *writer; char *error = spv_writer_open (fh_get_file_name (fh), &writer); diff --git a/src/output/tex.c b/src/output/tex.c index 4ffe33b1f9..8a88452ec8 100644 --- a/src/output/tex.c +++ b/src/output/tex.c @@ -108,15 +108,15 @@ tex_driver_cast (struct output_driver *driver) return UP_CAST (driver, struct tex_driver, driver); } -static struct driver_option * -opt (struct string_map *options, const char *key, const char *default_value) +static struct driver_option +opt (struct driver_options *options, const char *key, const char *default_value) { - return driver_option_get ("tex", options, key, default_value); + return driver_option_get (options, key, default_value); } static struct output_driver * tex_create (struct file_handle *fh, enum settings_output_devices device_type, - struct string_map *o) + struct driver_options *o) { FILE *file = fn_open (fh, "w"); if (!file)