+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);
+ }
+
+ struct file_handle *fh = fh_create_file (NULL, file_name, NULL, fh_default_properties ());
+
+ driver = f->create (fh, 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;
+}
+
+void
+output_driver_parse_option (const char *option, struct string_map *options)
+{
+ const char *equals = strchr (option, '=');
+ if (equals == NULL)
+ {
+ error (0, 0, _("%s: output option missing `='"), option);
+ return;
+ }
+
+ char *key = xmemdup0 (option, equals - option);
+ if (string_map_contains (options, key))
+ {
+ error (0, 0, _("%s: output option specified more than once"), key);
+ free (key);
+ return;
+ }
+
+ char *value = xmemdup0 (equals + 1, strlen (equals + 1));
+ string_map_insert_nocopy (options, key, value);
+}
+\f
+/* Extracts the actual text content from the given Pango MARKUP and returns it
+ as as a malloc()'d string. */
+char *
+output_get_text_from_markup (const char *markup)
+{
+ xmlParserCtxt *parser = xmlCreatePushParserCtxt (NULL, NULL, NULL, 0, NULL);
+ if (!parser)
+ return xstrdup (markup);
+
+ xmlParseChunk (parser, "<xml>", strlen ("<xml>"), false);
+ xmlParseChunk (parser, markup, strlen (markup), false);
+ xmlParseChunk (parser, "</xml>", strlen ("</xml>"), true);
+
+ char *content
+ = (parser->wellFormed
+ ? CHAR_CAST (char *,
+ xmlNodeGetContent (xmlDocGetRootElement (parser->myDoc)))
+ : xstrdup (markup));
+ xmlFreeDoc (parser->myDoc);
+ xmlFreeParserCtxt (parser);
+
+ return content;
+}
+
+char *
+output_driver_substitute_heading_vars (const char *src, int page_number)
+{
+ struct output_engine *e = engine_stack_top ();
+ struct string dst = DS_EMPTY_INITIALIZER;
+ ds_extend (&dst, strlen (src));
+ for (const char *p = src; *p;)
+ {
+ if (!strncmp (p, "&[", 6))
+ {
+ if (page_number != INT_MIN)
+ {
+ const char *start = p + 6;
+ const char *end = strchr (start, ']');
+ if (end)
+ {
+ const char *value = string_map_find__ (&e->heading_vars,
+ start, end - start);
+ if (value)
+ ds_put_cstr (&dst, value);
+ else if (ss_equals (ss_buffer (start, end - start),
+ ss_cstr ("Page")))
+ ds_put_format (&dst, "%d", page_number);
+ p = end + 1;
+ continue;
+ }
+ }
+ ds_put_cstr (&dst, "&");
+ p += 5;
+ }
+ else
+ ds_put_byte (&dst, *p++);
+ }
+ return ds_steal_cstr (&dst);
+}