/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000, 2007, 2009 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011, 2012, 2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <config.h>
-#include <output/driver.h>
-#include <output/driver-provider.h>
+#include "output/driver.h"
+#include "output/driver-provider.h"
#include <ctype.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
-#include <data/file-name.h>
-#include <data/settings.h>
-#include <libpspp/array.h>
-#include <libpspp/assertion.h>
-#include <libpspp/string-map.h>
-#include <libpspp/string-set.h>
-#include <libpspp/str.h>
-#include <output/output-item.h>
-#include <output/text-item.h>
-
-#include "error.h"
-#include "xalloc.h"
-#include "xmemdup0.h"
+#include "data/file-name.h"
+#include "data/settings.h"
+#include "libpspp/array.h"
+#include "libpspp/assertion.h"
+#include "libpspp/message.h"
+#include "libpspp/llx.h"
+#include "libpspp/string-map.h"
+#include "libpspp/string-set.h"
+#include "libpspp/str.h"
+#include "output/message-item.h"
+#include "output/output-item.h"
+#include "output/text-item.h"
+
+#include "gl/xalloc.h"
+#include "gl/xmemdup0.h"
#include "gettext.h"
#define _(msgid) gettext (msgid)
-static const struct output_driver_class *driver_classes[];
-
-static struct output_driver **drivers;
-static size_t n_drivers, allocated_drivers;
+struct output_engine
+ {
+ struct llx_list drivers; /* Contains "struct output_driver"s. */
+ struct string deferred_syntax; /* TEXT_ITEM_SYNTAX being accumulated. */
+ };
-static unsigned int enabled_device_types = ((1u << OUTPUT_DEVICE_UNKNOWN)
- | (1u << OUTPUT_DEVICE_LISTING)
- | (1u << OUTPUT_DEVICE_SCREEN)
- | (1u << OUTPUT_DEVICE_PRINTER));
+static const struct output_driver_factory *factories[];
-static struct output_item *deferred_syntax;
-static bool in_command;
+/* A stack of output engines.. */
+static struct output_engine *engine_stack;
+static size_t n_stack, allocated_stack;
-void
-output_close (void)
+static struct output_engine *
+engine_stack_top (void)
{
- while (n_drivers > 0)
- {
- struct output_driver *d = drivers[--n_drivers];
- output_driver_destroy (d);
- }
+ assert (n_stack > 0);
+ return &engine_stack[n_stack - 1];
}
-static void
-expand_macro (const char *name, struct string *dst, void *macros_)
+void
+output_engine_push (void)
{
- const struct string_map *macros = macros_;
+ struct output_engine *e;
- if (!strcmp (name, "viewwidth"))
- ds_put_format (dst, "%d", settings_get_viewwidth ());
- else if (!strcmp (name, "viewlength"))
- ds_put_format (dst, "%d", settings_get_viewlength ());
- else
- {
- const char *value = string_map_find (macros, name);
- if (value != NULL)
- ds_put_cstr (dst, value);
- }
+ if (n_stack >= allocated_stack)
+ engine_stack = x2nrealloc (engine_stack, &allocated_stack,
+ sizeof *engine_stack);
+
+ e = &engine_stack[n_stack++];
+ llx_init (&e->drivers);
+ ds_init_empty (&e->deferred_syntax);
}
-/* Defines one configuration macro based on the text in BP, which
- should be of the form `KEY=VALUE'. Returns true if
- successful, false if S is not in the proper form. */
-bool
-output_define_macro (const char *s, struct string_map *macros)
+void
+output_engine_pop (void)
{
- const char *key_start, *value;
- size_t key_len;
- char *key;
-
- s += strspn (s, CC_SPACES);
-
- key_start = s;
- key_len = strcspn (s, "=" CC_SPACES);
- if (key_len == 0)
- return false;
- s += key_len;
-
- s += strspn (s, CC_SPACES);
- if (*s == '=')
- s++;
-
- s += strspn (s, CC_SPACES);
- value = s;
+ struct output_engine *e;
- key = xmemdup0 (key_start, key_len);
- if (!string_map_contains (macros, key))
+ assert (n_stack > 0);
+ e = &engine_stack[--n_stack];
+ while (!llx_is_empty (&e->drivers))
{
- struct string expanded_value = DS_EMPTY_INITIALIZER;
-
- fn_interp_vars (ss_cstr (value), expand_macro, ¯os, &expanded_value);
- string_map_insert_nocopy (macros, key, ds_steal_cstr (&expanded_value));
+ struct output_driver *d = llx_pop_head (&e->drivers, &llx_malloc_mgr);
+ output_driver_destroy (d);
}
- else
- free (key);
-
- return true;
+ ds_destroy (&e->deferred_syntax);
}
-static void
-add_driver_names (char *to, struct string_set *names)
+void
+output_get_supported_formats (struct string_set *formats)
{
- char *save_ptr = NULL;
- char *name;
+ const struct output_driver_factory **fp;
- for (name = strtok_r (to, CC_SPACES, &save_ptr); name != NULL;
- name = strtok_r (NULL, CC_SPACES, &save_ptr))
- string_set_insert (names, name);
+ for (fp = factories; *fp != NULL; fp++)
+ string_set_insert (formats, (*fp)->extension);
}
static void
-init_default_drivers (void)
+output_submit__ (struct output_engine *e, struct output_item *item)
{
- error (0, 0, _("using default output driver configuration"));
- output_configure_driver ("list:ascii:listing:"
- "length=66 width=79 output-file=\"pspp.list\"");
-}
-
-static void
-warn_unused_drivers (const struct string_set *unused_drivers,
- const struct string_set *requested_drivers)
-{
- const struct string_set_node *node;
- const char *name;
-
- STRING_SET_FOR_EACH (name, node, unused_drivers)
- if (string_set_contains (requested_drivers, name))
- error (0, 0, _("unknown output driver `%s'"), name);
- else
- error (0, 0, _("output driver `%s' referenced but never defined"), name);
-}
-
-void
-output_read_configuration (const struct string_map *macros_,
- const struct string_set *driver_names_)
-{
- struct string_map macros = STRING_MAP_INITIALIZER (macros);
- struct string_set driver_names = STRING_SET_INITIALIZER (driver_names);
- char *devices_file_name = NULL;
- FILE *devices_file = NULL;
- struct string line = DS_EMPTY_INITIALIZER;
- int line_number;
-
- ds_init_empty (&line);
-
- devices_file_name = fn_search_path ("devices", config_path);
- if (devices_file_name == NULL)
- {
- error (0, 0, _("cannot find output initialization file "
- "(use `-vv' to view search path)"));
- goto exit;
- }
- devices_file = fopen (devices_file_name, "r");
- if (devices_file == NULL)
- {
- error (0, errno, _("cannot open \"%s\""), devices_file_name);
- goto exit;
- }
-
- string_map_replace_map (¯os, macros_);
- string_set_union (&driver_names, driver_names_);
- if (string_set_is_empty (&driver_names))
- string_set_insert (&driver_names, "default");
-
- line_number = 0;
- for (;;)
- {
- char *cp, *delimiter, *name;
-
- if (!ds_read_config_line (&line, &line_number, devices_file))
- {
- if (ferror (devices_file))
- error (0, errno, _("reading \"%s\""), devices_file_name);
- break;
- }
-
- cp = ds_cstr (&line);
- cp += strspn (cp, CC_SPACES);
-
- if (*cp == '\0')
- continue;
- else if (!strncmp ("define", cp, 6) && isspace ((unsigned char) cp[6]))
- {
- if (!output_define_macro (&cp[7], ¯os))
- error_at_line (0, 0, devices_file_name, line_number,
- _("\"%s\" is not a valid macro definition"),
- &cp[7]);
- continue;
- }
-
- delimiter = cp + strcspn (cp, ":=");
- name = xmemdup0 (cp, delimiter - cp);
- if (*delimiter == '=')
- {
- if (string_set_delete (&driver_names, name))
- add_driver_names (delimiter + 1, &driver_names);
- }
- else if (*delimiter == ':')
- {
- if (string_set_delete (&driver_names, name))
- {
- fn_interp_vars (ds_ss (&line), expand_macro, ¯os, &line);
- output_configure_driver (ds_cstr (&line));
- }
- }
- else
- error_at_line (0, 0, devices_file_name, line_number,
- _("syntax error"));
- free (name);
- }
-
- warn_unused_drivers (&driver_names, driver_names_);
-
-exit:
- if (devices_file != NULL)
- fclose (devices_file);
- free (devices_file_name);
- ds_destroy (&line);
- string_set_destroy (&driver_names);
- string_map_destroy (¯os);
+ struct llx *llx, *next;
- if (n_drivers == 0)
+ for (llx = llx_head (&e->drivers); llx != llx_null (&e->drivers); llx = next)
{
- error (0, 0, _("no active output drivers"));
- init_default_drivers ();
- }
-}
-
-/* Obtains a token from S and advances its position. Errors are
- reported against the given DRIVER_NAME.
- The token is stored in TOKEN. Returns true if successful,
- false on syntax error.
-
- Caller is responsible for skipping leading spaces. */
-static bool
-get_option_token (char **s_, const char *driver_name,
- struct string *token)
-{
- struct substring s = ss_cstr (*s_);
- int c;
+ struct output_driver *d = llx_data (llx);
+ enum settings_output_type type;
- ds_clear (token);
- c = ss_get_char (&s);
- if (c == EOF)
- {
- error (0, 0, _("syntax error parsing options for \"%s\" driver"),
- driver_name);
- return false;
- }
- else if (c == '\'' || c == '"')
- {
- int quote = c;
+ next = llx_next (llx);
- for (;;)
+ if (is_message_item (item))
{
- c = ss_get_char (&s);
- if (c == quote)
- break;
- else if (c == EOF)
- {
- error (0, 0,
- _("reached end of options inside quoted string "
- "parsing options for \"%s\" driver"),
- driver_name);
- return false;
- }
- else if (c != '\\')
- ds_put_char (token, c);
+ const struct msg *m = message_item_get_msg (to_message_item (item));
+ if (m->severity == MSG_S_NOTE)
+ type = SETTINGS_OUTPUT_NOTE;
else
- {
- int out;
-
- c = ss_get_char (&s);
- switch (c)
- {
- case '\'':
- out = '\'';
- break;
- case '"':
- out = '"';
- break;
- case '\\':
- out = '\\';
- break;
- case 'a':
- out = '\a';
- break;
- case 'b':
- out = '\b';
- break;
- case 'f':
- out = '\f';
- break;
- case 'n':
- out = '\n';
- break;
- case 'r':
- out = '\r';
- break;
- case 't':
- out = '\t';
- break;
- case 'v':
- out = '\v';
- break;
- case '0':
- case '1':
- case '2':
- case '3':
- case '4':
- case '5':
- case '6':
- case '7':
- out = c - '0';
- while (ss_first (s) >= '0' && ss_first (s) <= '7')
- out = out * 8 + (ss_get_char (&s) - '0');
- break;
- case 'x':
- case 'X':
- out = 0;
- while (isxdigit (ss_first (s)))
- {
- c = ss_get_char (&s);
- out *= 16;
- if (isdigit (c))
- out += c - '0';
- else
- out += tolower (c) - 'a' + 10;
- }
- break;
- default:
- error (0, 0, _("syntax error in string constant "
- "parsing options for \"%s\" driver"),
- driver_name);
- return false;
- }
- ds_put_char (token, out);
- }
+ type = SETTINGS_OUTPUT_ERROR;
}
- }
- else
- {
- for (;;)
- {
- ds_put_char (token, c);
-
- c = ss_first (s);
- if (c == EOF || c == '=' || isspace (c))
- break;
- ss_advance (&s, 1);
- }
- }
-
- *s_ = s.string;
- return 1;
-}
-
-static void
-parse_options (const char *driver_name, char *options,
- struct string_map *option_map)
-{
- struct string key = DS_EMPTY_INITIALIZER;
- struct string value = DS_EMPTY_INITIALIZER;
-
- for (;;)
- {
- options += strspn (options, CC_SPACES);
- if (*options == '\0')
- break;
-
- if (!get_option_token (&options, driver_name, &key))
- break;
-
- options += strspn (options, CC_SPACES);
- if (*options != '=')
- {
- error (0, 0, _("syntax error expecting `=' "
- "parsing options for driver \"%s\""),
- driver_name);
- break;
- }
- options++;
-
- options += strspn (options, CC_SPACES);
- if (!get_option_token (&options, driver_name, &value))
- break;
-
- if (string_map_contains (option_map, ds_cstr (&key)))
- error (0, 0, _("driver \"%s\" defines option \"%s\" multiple times"),
- driver_name, ds_cstr (&key));
- else
- string_map_insert (option_map, ds_cstr (&key), ds_cstr (&value));
- }
-
- ds_destroy (&key);
- ds_destroy (&value);
-}
-
-static char *
-trim_token (char *token)
-{
- if (token != NULL)
- {
- char *end;
-
- /* Trim leading spaces. */
- while (isspace ((unsigned char) *token))
- token++;
-
- /* Trim trailing spaces. */
- end = strchr (token, '\0');
- while (end > token && isspace ((unsigned char) end[-1]))
- *--end = '\0';
-
- /* An empty token is a null token. */
- if (*token == '\0')
- return NULL;
- }
- return token;
-}
-
-static const struct output_driver_class *
-find_output_class (const char *name)
-{
- const struct output_driver_class **classp;
-
- for (classp = driver_classes; *classp != NULL; classp++)
- if (!strcmp ((*classp)->name, name))
- break;
-
- return *classp;
-}
-
-static struct output_driver *
-create_driver (const char *driver_name, const char *class_name,
- const char *device_type, struct string_map *options)
-{
- const struct output_driver_class *class;
- enum output_device_type type;
- struct output_driver *driver;
-
- type = OUTPUT_DEVICE_UNKNOWN;
- if (device_type != NULL && device_type[0] != '\0')
- {
- if (!strcmp (device_type, "listing"))
- type = OUTPUT_DEVICE_LISTING;
- else if (!strcmp (device_type, "screen"))
- type = OUTPUT_DEVICE_SCREEN;
- else if (!strcmp (device_type, "printer"))
- type = OUTPUT_DEVICE_PRINTER;
+ else if (is_text_item (item)
+ && text_item_get_type (to_text_item (item)) == TEXT_ITEM_SYNTAX)
+ type = SETTINGS_OUTPUT_SYNTAX;
else
- error (0, 0, _("unknown device type `%s'"), device_type);
- }
+ type = SETTINGS_OUTPUT_RESULT;
- class = find_output_class (class_name);
- if (class != NULL)
- driver = class->create (driver_name, type, options);
- else
- {
- error (0, 0, _("unknown output driver class `%s'"), class_name);
- driver = NULL;
+ if (settings_get_output_routing (type) & d->device_type)
+ d->class->submit (d, item);
}
- string_map_destroy (options);
-
- return driver;
-}
-
-struct output_driver *
-output_driver_create (const char *class_name, struct string_map *options)
-{
- return create_driver (class_name, class_name, NULL, options);
+ output_item_unref (item);
}
-/* String LINE is in format:
- DRIVERNAME:CLASSNAME:DEVICETYPE:OPTIONS
-*/
-void
-output_configure_driver (const char *line_)
+static void
+flush_deferred_syntax (struct output_engine *e)
{
- char *save_ptr = NULL;
- char *line = xstrdup (line_);
- char *driver_name = trim_token (strtok_r (line, ":", &save_ptr));
- char *class_name = trim_token (strtok_r (NULL, ":", &save_ptr));
- char *device_type = trim_token (strtok_r (NULL, ":", &save_ptr));
- char *options = trim_token (strtok_r (NULL, "", &save_ptr));
-
- if (driver_name && class_name)
+ if (!ds_is_empty (&e->deferred_syntax))
{
- struct string_map option_map;
- struct output_driver *driver;
-
- string_map_init (&option_map);
- if (options != NULL)
- parse_options (driver_name, options, &option_map);
-
- driver = create_driver (driver_name, class_name,
- device_type, &option_map);
- if (driver != NULL)
- output_driver_register (driver);
+ char *syntax = ds_steal_cstr (&e->deferred_syntax);
+ output_submit__ (e, text_item_super (
+ text_item_create_nocopy (TEXT_ITEM_SYNTAX, syntax)));
}
- else
- error (0, 0,
- _("driver definition line missing driver name or class name"));
-
- free (line);
-}
-
-/* Display on stdout a list of all registered driver classes. */
-void
-output_list_classes (void)
-{
- const struct output_driver_class **classp;
-
- printf (_("Driver classes:"));
- for (classp = driver_classes; *classp != NULL; classp++)
- printf (" %s", (*classp)->name);
- putc ('\n', stdout);
}
static bool
-driver_is_enabled (const struct output_driver *d)
+is_syntax_item (const struct output_item *item)
{
- return (1u << d->device_type) & enabled_device_types;
-}
-
-static void
-output_submit__ (struct output_item *item)
-{
- size_t i;
-
- for (i = 0; i < n_drivers; i++)
- {
- struct output_driver *d = drivers[i];
- if (driver_is_enabled (d))
- d->class->submit (d, item);
- }
-
- output_item_unref (item);
+ return (is_text_item (item)
+ && text_item_get_type (to_text_item (item)) == TEXT_ITEM_SYNTAX);
}
/* Submits ITEM to the configured output drivers, and transfers ownership to
void
output_submit (struct output_item *item)
{
- if (is_text_item (item))
+ struct output_engine *e = engine_stack_top ();
+
+ if (is_syntax_item (item))
{
- struct text_item *text = to_text_item (item);
- switch (text_item_get_type (text))
- {
- case TEXT_ITEM_SYNTAX:
- if (!in_command)
- {
- if (deferred_syntax != NULL)
- output_submit__ (deferred_syntax);
- deferred_syntax = item;
- return;
- }
- break;
-
- case TEXT_ITEM_COMMAND_OPEN:
- output_submit__ (item);
- if (deferred_syntax != NULL)
- {
- output_submit__ (deferred_syntax);
- deferred_syntax = NULL;
- }
- in_command = true;
- return;
-
- case TEXT_ITEM_COMMAND_CLOSE:
- in_command = false;
- break;
-
- default:
- break;
- }
+ ds_put_cstr (&e->deferred_syntax, text_item_get_text (to_text_item (item)));
+ output_item_unref (item);
+ return;
}
- output_submit__ (item);
+ flush_deferred_syntax (e);
+ output_submit__ (e, item);
}
/* Flushes output to screen devices, so that the user can see
void
output_flush (void)
{
- size_t i;
+ struct output_engine *e = engine_stack_top ();
+ struct llx *llx;
- for (i = 0; i < n_drivers; i++)
+ flush_deferred_syntax (e);
+ for (llx = llx_head (&e->drivers); llx != llx_null (&e->drivers);
+ llx = llx_next (llx))
{
- struct output_driver *d = drivers[i];
- if (driver_is_enabled (d) && d->class->flush != NULL)
+ struct output_driver *d = llx_data (llx);
+ if (d->device_type & SETTINGS_DEVICE_TERMINAL && d->class->flush != NULL)
d->class->flush (d);
}
}
-
-unsigned int
-output_get_enabled_types (void)
-{
- return enabled_device_types;
-}
-
-void
-output_set_enabled_types (unsigned int types)
-{
- enabled_device_types = types;
-}
-
-void
-output_set_type_enabled (bool enable, enum output_device_type type)
-{
- unsigned int bit = 1u << type;
- if (enable)
- enabled_device_types |= bit;
- else
- enabled_device_types |= ~bit;
-}
\f
void
output_driver_init (struct output_driver *driver,
const struct output_driver_class *class,
- const char *name, enum output_device_type type)
+ const char *name, enum settings_output_devices type)
{
driver->class = class;
driver->name = xstrdup (name);
return driver->name;
}
\f
+static struct output_engine *
+output_driver_get_engine (const struct output_driver *driver)
+{
+ struct output_engine *e;
+
+ for (e = engine_stack; e < &engine_stack[n_stack]; e++)
+ if (llx_find (llx_head (&e->drivers), llx_null (&e->drivers), driver))
+ return e;
+
+ return NULL;
+}
+
void
output_driver_register (struct output_driver *driver)
{
+ struct output_engine *e = engine_stack_top ();
+
assert (!output_driver_is_registered (driver));
- if (n_drivers >= allocated_drivers)
- drivers = x2nrealloc (drivers, &allocated_drivers, sizeof *drivers);
- drivers[n_drivers++] = driver;
+ llx_push_tail (&e->drivers, driver, &llx_malloc_mgr);
}
void
output_driver_unregister (struct output_driver *driver)
{
- size_t i;
-
- for (i = 0; i < n_drivers; i++)
- if (drivers[i] == driver)
- {
- remove_element (drivers, n_drivers, sizeof *drivers, i);
- return;
- }
- NOT_REACHED ();
+ struct output_engine *e = output_driver_get_engine (driver);
+
+ assert (e != NULL);
+ llx_remove (llx_find (llx_head (&e->drivers), llx_null (&e->drivers), driver),
+ &llx_malloc_mgr);
}
bool
output_driver_is_registered (const struct output_driver *driver)
{
- size_t i;
+ return output_driver_get_engine (driver) != NULL;
+}
+\f
+/* Useful functions for output driver implementation. */
- for (i = 0; i < n_drivers; i++)
- if (drivers[i] == driver)
- return true;
- return false;
+void
+output_driver_track_current_command (const struct output_item *output_item,
+ char **command_namep)
+{
+ if (is_text_item (output_item))
+ {
+ const struct text_item *item = to_text_item (output_item);
+ const char *text = text_item_get_text (item);
+ enum text_item_type type = text_item_get_type (item);
+
+ if (type == TEXT_ITEM_COMMAND_OPEN)
+ {
+ free (*command_namep);
+ *command_namep = xstrdup (text);
+ }
+ else if (type == TEXT_ITEM_COMMAND_CLOSE)
+ {
+ free (*command_namep);
+ *command_namep = NULL;
+ }
+ }
}
\f
-/* Known driver classes. */
+extern const struct output_driver_factory txt_driver_factory;
+extern const struct output_driver_factory list_driver_factory;
+extern const struct output_driver_factory html_driver_factory;
+extern const struct output_driver_factory csv_driver_factory;
+#ifdef ODF_WRITE_SUPPORT
+extern const struct output_driver_factory odt_driver_factory;
+#endif
+#ifdef HAVE_CAIRO
+extern const struct output_driver_factory pdf_driver_factory;
+extern const struct output_driver_factory ps_driver_factory;
+extern const struct output_driver_factory svg_driver_factory;
+#endif
-static const struct output_driver_class *driver_classes[] =
+static const struct output_driver_factory *factories[] =
{
- &ascii_class,
+ &txt_driver_factory,
+ &list_driver_factory,
+ &html_driver_factory,
+ &csv_driver_factory,
+#ifdef ODF_WRITE_SUPPORT
+ &odt_driver_factory,
+#endif
#ifdef HAVE_CAIRO
- &cairo_class,
+ &pdf_driver_factory,
+ &ps_driver_factory,
+ &svg_driver_factory,
#endif
- &html_class,
- &odt_class,
- &csv_class,
- NULL,
+ NULL
};
+static const struct output_driver_factory *
+find_factory (const char *format)
+{
+ const struct output_driver_factory **fp;
+
+ for (fp = factories; *fp != NULL; fp++)
+ {
+ const struct output_driver_factory *f = *fp;
+
+ if (!strcmp (f->extension, format))
+ return f;
+ }
+ return &txt_driver_factory;
+}
+
+static enum settings_output_devices
+default_device_type (const char *file_name)
+{
+ return (!strcmp (file_name, "-")
+ ? SETTINGS_DEVICE_TERMINAL
+ : SETTINGS_DEVICE_LISTING);
+}
+
+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)
+ {
+ if (file_name != NULL)
+ {
+ const char *extension = strrchr (file_name, '.');
+ format = xstrdup (extension != NULL ? extension + 1 : "");
+ }
+ else
+ format = xstrdup ("txt");
+ }
+ f = find_factory (format);
+
+ 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);
+ }
+
+ driver = f->create (file_name, device_type, options);
+ if (driver != NULL)
+ {
+ const struct string_map_node *node;
+ const char *key;
+
+ STRING_MAP_FOR_EACH_KEY (key, node, options)
+ msg (MW, _("%s: unknown option `%s'"), file_name, key);
+ }
+ string_map_clear (options);
+
+ free (file_name);
+ free (format);
+ free (device_string);
+
+ return driver;
+}