+
+static void
+add_languages (string_list_ty *languages, string_list_ty *desired_languages,
+ const char *line, size_t length)
+{
+ char *start;
+
+ /* Split the line by whitespace and build the languages list. */
+ for (start = (char *) line; start - line < length; )
+ {
+ char *p;
+
+ /* Skip whitespace before the string. */
+ while (*start == ' ' || *start == '\t')
+ start++;
+
+ p = start;
+ while (*p != '\0' && *p != ' ' && *p != '\t')
+ p++;
+
+ *p = '\0';
+ if (desired_languages == NULL
+ || string_list_member (desired_languages, start))
+ string_list_append_unique (languages, start);
+ start = p + 1;
+ }
+}
+
+/* Compute the languages list by reading the "LINGUAS" envvar or the
+ LINGUAS file under DIRECTORY. */
+static void
+get_languages (string_list_ty *languages, const char *directory)
+{
+ char *envval;
+ string_list_ty real_desired_languages, *desired_languages = NULL;
+ char *linguas_file_name = NULL;
+ struct stat statbuf;
+ FILE *fp;
+ size_t line_len = 0;
+ char *line_buf = NULL;
+
+ envval = getenv ("LINGUAS");
+ if (envval)
+ {
+ string_list_init (&real_desired_languages);
+ add_languages (&real_desired_languages, NULL, envval, strlen (envval));
+ desired_languages = &real_desired_languages;
+ }
+
+ linguas_file_name = xconcatenated_filename (directory, "LINGUAS", NULL);
+ if (stat (linguas_file_name, &statbuf) < 0)
+ {
+ error (EXIT_SUCCESS, 0, _("%s does not exist"), linguas_file_name);
+ goto out;
+ }
+
+ fp = fopen (linguas_file_name, "r");
+ if (fp == NULL)
+ {
+ error (EXIT_SUCCESS, 0, _("%s exists but cannot read"),
+ linguas_file_name);
+ goto out;
+ }
+
+ while (!feof (fp))
+ {
+ /* Read next line from file. */
+ int len = getline (&line_buf, &line_len, fp);
+
+ /* In case of an error leave loop. */
+ if (len < 0)
+ break;
+
+ /* Remove trailing '\n' and trailing whitespace. */
+ if (len > 0 && line_buf[len - 1] == '\n')
+ line_buf[--len] = '\0';
+ while (len > 0
+ && (line_buf[len - 1] == ' '
+ || line_buf[len - 1] == '\t'
+ || line_buf[len - 1] == '\r'))
+ line_buf[--len] = '\0';
+
+ /* Test if we have to ignore the line. */
+ if (*line_buf == '\0' || *line_buf == '#')
+ continue;
+
+ add_languages (languages, desired_languages, line_buf, len);
+ }
+
+ free (line_buf);
+ fclose (fp);
+
+ out:
+ if (desired_languages != NULL)
+ string_list_destroy (desired_languages);
+ free (linguas_file_name);
+}
+
+static void
+msgfmt_operand_list_init (msgfmt_operand_list_ty *operands)
+{
+ operands->items = NULL;
+ operands->nitems = 0;
+ operands->nitems_max = 0;
+}
+
+static void
+msgfmt_operand_list_destroy (msgfmt_operand_list_ty *operands)
+{
+ size_t i;
+
+ for (i = 0; i < operands->nitems; i++)
+ {
+ free (operands->items[i].language);
+ message_list_free (operands->items[i].mlp, 0);
+ }
+ free (operands->items);
+}
+
+static void
+msgfmt_operand_list_append (msgfmt_operand_list_ty *operands,
+ const char *language,
+ message_list_ty *messages)
+{
+ msgfmt_operand_ty *operand;
+
+ if (operands->nitems == operands->nitems_max)
+ {
+ operands->nitems_max = operands->nitems_max * 2 + 1;
+ operands->items = xrealloc (operands->items,
+ sizeof (msgfmt_operand_ty)
+ * operands->nitems_max);
+ }
+
+ operand = &operands->items[operands->nitems++];
+ operand->language = xstrdup (language);
+ operand->mlp = messages;
+}
+
+static int
+msgfmt_operand_list_add_from_directory (msgfmt_operand_list_ty *operands,
+ const char *directory)
+{
+ string_list_ty languages;
+ void *saved_dir_list;
+ int retval = 0;
+ size_t i;
+
+ string_list_init (&languages);
+ get_languages (&languages, directory);
+
+ if (languages.nitems == 0)
+ return 0;
+
+ /* Reset the directory search list so only .po files under DIRECTORY
+ will be read. */
+ saved_dir_list = dir_list_save_reset ();
+ dir_list_append (directory);
+
+ /* Read all .po files. */
+ for (i = 0; i < languages.nitems; i++)
+ {
+ const char *language = languages.item[i];
+ message_list_ty *mlp;
+ char *input_file_name;
+ int nerrors;
+
+ current_domain = new_domain (MESSAGE_DOMAIN_DEFAULT,
+ add_mo_suffix (MESSAGE_DOMAIN_DEFAULT));
+
+ input_file_name = xconcatenated_filename ("", language, ".po");
+ read_catalog_file_msgfmt (input_file_name, &input_format_po);
+ free (input_file_name);
+
+ /* The domain directive is not supported in the bulk execution mode.
+ Thus, domain_list should always contain a single domain. */
+ assert (current_domain == domain_list && domain_list->next == NULL);
+ mlp = current_domain->mlp;
+ free (current_domain);
+ current_domain = domain_list = NULL;
+
+ /* Remove obsolete messages. They were only needed for duplicate
+ checking. */
+ message_list_remove_if_not (mlp, is_nonobsolete);
+
+ /* Perform all kinds of checks: plural expressions, format
+ strings, ... */
+ nerrors =
+ check_message_list (mlp,
+ /* Untranslated and fuzzy messages have already
+ been dealt with during parsing, see below in
+ msgfmt_frob_new_message. */
+ 0, 0,
+ 1, check_format_strings, check_header,
+ check_compatibility,
+ check_accelerators, accelerator_char);
+
+ retval += nerrors;
+ if (nerrors > 0)
+ {
+ error (0, 0,
+ ngettext ("found %d fatal error", "found %d fatal errors",
+ nerrors),
+ nerrors);
+ continue;
+ }
+
+ /* Convert the messages to Unicode. */
+ iconv_message_list (mlp, NULL, po_charset_utf8, NULL);
+
+ msgfmt_operand_list_append (operands, language, mlp);
+ }
+
+ string_list_destroy (&languages);
+ dir_list_restore (saved_dir_list);
+
+ return retval;
+}
+
+/* Helper function to support 'bulk' operation mode of --desktop.
+ This reads all .po files in DIRECTORY and merges them into a
+ .desktop file FILE_NAME. Currently it does not support some
+ options available in 'iterative' mode, such as --statistics. */
+static int
+msgfmt_desktop_bulk (const char *directory,
+ const char *template_file_name,
+ hash_table *keywords,
+ const char *file_name)
+{
+ msgfmt_operand_list_ty operands;
+ int nerrors, status;
+
+ msgfmt_operand_list_init (&operands);
+
+ /* Read all .po files. */
+ nerrors = msgfmt_operand_list_add_from_directory (&operands, directory);
+ if (nerrors > 0)
+ {
+ msgfmt_operand_list_destroy (&operands);
+ return 1;
+ }
+
+ /* Write the messages into .desktop file. */
+ status = msgdomain_write_desktop_bulk (&operands,
+ template_file_name,
+ keywords,
+ file_name);
+
+ msgfmt_operand_list_destroy (&operands);
+
+ return status;
+}
+
+/* Helper function to support 'bulk' operation mode of --xml.
+ This reads all .po files in DIRECTORY and merges them into an
+ XML file FILE_NAME. Currently it does not support some
+ options available in 'iterative' mode, such as --statistics. */
+static int
+msgfmt_xml_bulk (const char *directory,
+ const char *template_file_name,
+ its_rule_list_ty *its_rules,
+ const char *file_name)
+{
+ msgfmt_operand_list_ty operands;
+ int nerrors, status;
+
+ msgfmt_operand_list_init (&operands);
+
+ /* Read all .po files. */
+ nerrors = msgfmt_operand_list_add_from_directory (&operands, directory);
+ if (nerrors > 0)
+ {
+ msgfmt_operand_list_destroy (&operands);
+ return 1;
+ }
+
+ /* Write the messages into .xml file. */
+ status = msgdomain_write_xml_bulk (&operands,
+ template_file_name,
+ its_rules,
+ file_name);
+
+ msgfmt_operand_list_destroy (&operands);
+
+ return status;
+}