1 /* Writing Desktop Entry files.
2 Copyright (C) 1995-1998, 2000-2003, 2005-2006, 2008-2009, 2014-2016, 2019-2020 Free Software Foundation, Inc.
3 This file was written by Daiki Ueno <ueno@gnu.org>.
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 /* Specification. */
23 #include "write-desktop.h"
24
25 #include <assert.h>
26 #include <errno.h>
27 #include <stdint.h>
28 #include <stdlib.h>
29 #include <stdio.h>
30 #include "error.h"
31 #include "msgl-iconv.h"
32 #include "msgl-header.h"
33 #include "po-charset.h"
34 #include "read-catalog.h"
35 #include "read-po.h"
36 #include "read-desktop.h"
37 #include "fwriteerror.h"
38 #include "xalloc.h"
39 #include "gettext.h"
40
41 #define _(str) gettext (str)
42
43 typedef struct msgfmt_desktop_reader_ty msgfmt_desktop_reader_ty;
44 struct msgfmt_desktop_reader_ty
45 {
46 DESKTOP_READER_TY
47 msgfmt_operand_list_ty *operands;
48 hash_table *keywords;
49 FILE *output_file;
50 };
51
52 static void
msgfmt_desktop_handle_group(struct desktop_reader_ty * reader,const char * group)53 msgfmt_desktop_handle_group (struct desktop_reader_ty *reader,
54 const char *group)
55 {
56 msgfmt_desktop_reader_ty *msgfmt_reader = (msgfmt_desktop_reader_ty *) reader;
57
58 fprintf (msgfmt_reader->output_file, "[%s]\n", group);
59 }
60
61 static void
msgfmt_desktop_handle_pair(desktop_reader_ty * reader,lex_pos_ty * key_pos,const char * key,const char * locale,const char * value)62 msgfmt_desktop_handle_pair (desktop_reader_ty *reader,
63 lex_pos_ty *key_pos,
64 const char *key,
65 const char *locale,
66 const char *value)
67 {
68 msgfmt_desktop_reader_ty *msgfmt_reader = (msgfmt_desktop_reader_ty *) reader;
69 void *keyword_value;
70
71 if (!locale)
72 {
73 /* Write translated pair, if any. */
74 if (hash_find_entry (msgfmt_reader->keywords, key, strlen (key),
75 &keyword_value) == 0)
76 {
77 bool is_list = (bool) (uintptr_t) keyword_value;
78 char *unescaped = desktop_unescape_string (value, is_list);
79 size_t i;
80
81 for (i = 0; i < msgfmt_reader->operands->nitems; i++)
82 {
83 msgfmt_operand_ty *operand = &msgfmt_reader->operands->items[i];
84 message_ty *mp;
85
86 mp = message_list_search (operand->mlp, NULL, unescaped);
87 if (mp && *mp->msgstr != '\0')
88 {
89 char *escaped;
90
91 escaped = desktop_escape_string (mp->msgstr, is_list);
92 fprintf (msgfmt_reader->output_file,
93 "%s[%s]=%s\n",
94 key, operand->language, escaped);
95 free (escaped);
96 }
97 }
98 free (unescaped);
99 }
100
101 /* Write untranslated pair. */
102 fprintf (msgfmt_reader->output_file, "%s=%s\n", key, value);
103 }
104 else
105 /* Preserve already translated pair. */
106 fprintf (msgfmt_reader->output_file, "%s[%s]=%s\n", key, locale, value);
107 }
108
109 static void
msgfmt_desktop_handle_comment(struct desktop_reader_ty * reader,const char * s)110 msgfmt_desktop_handle_comment (struct desktop_reader_ty *reader, const char *s)
111 {
112 msgfmt_desktop_reader_ty *msgfmt_reader = (msgfmt_desktop_reader_ty *) reader;
113
114 fputc ('#', msgfmt_reader->output_file);
115 fputs (s, msgfmt_reader->output_file);
116 fputc ('\n', msgfmt_reader->output_file);
117 }
118
119 static void
msgfmt_desktop_handle_blank(struct desktop_reader_ty * reader,const char * s)120 msgfmt_desktop_handle_blank (struct desktop_reader_ty *reader, const char *s)
121 {
122 msgfmt_desktop_reader_ty *msgfmt_reader = (msgfmt_desktop_reader_ty *) reader;
123
124 fputs (s, msgfmt_reader->output_file);
125 fputc ('\n', msgfmt_reader->output_file);
126 }
127
128 desktop_reader_class_ty msgfmt_methods =
129 {
130 sizeof (msgfmt_desktop_reader_ty),
131 NULL,
132 NULL,
133 msgfmt_desktop_handle_group,
134 msgfmt_desktop_handle_pair,
135 msgfmt_desktop_handle_comment,
136 msgfmt_desktop_handle_blank
137 };
138
139 int
msgdomain_write_desktop_bulk(msgfmt_operand_list_ty * operands,const char * template_file_name,hash_table * keywords,const char * file_name)140 msgdomain_write_desktop_bulk (msgfmt_operand_list_ty *operands,
141 const char *template_file_name,
142 hash_table *keywords,
143 const char *file_name)
144 {
145 desktop_reader_ty *reader;
146 msgfmt_desktop_reader_ty *msgfmt_reader;
147 FILE *template_file;
148
149 reader = desktop_reader_alloc (&msgfmt_methods);
150 msgfmt_reader = (msgfmt_desktop_reader_ty *) reader;
151
152 msgfmt_reader->operands = operands;
153 msgfmt_reader->keywords = keywords;
154
155 if (strcmp (file_name, "-") == 0)
156 msgfmt_reader->output_file = stdout;
157 else
158 {
159 msgfmt_reader->output_file = fopen (file_name, "w");
160 if (msgfmt_reader->output_file == NULL)
161 {
162 desktop_reader_free (reader);
163 error (0, errno, _("error while opening \"%s\" for writing"),
164 file_name);
165 return 1;
166 }
167 }
168
169 template_file = fopen (template_file_name, "r");
170 if (template_file == NULL)
171 {
172 desktop_reader_free (reader);
173 error (0, errno, _("error while opening \"%s\" for reading"),
174 template_file_name);
175 return 1;
176 }
177
178 desktop_parse (reader, template_file, template_file_name, template_file_name);
179
180 /* Make sure nothing went wrong. */
181 if (fwriteerror (msgfmt_reader->output_file))
182 {
183 error (0, errno, _("error while writing \"%s\" file"),
184 file_name);
185 return 1;
186 }
187
188 desktop_reader_free (reader);
189
190 return 0;
191 }
192
193 int
msgdomain_write_desktop(message_list_ty * mlp,const char * canon_encoding,const char * locale_name,const char * template_file_name,hash_table * keywords,const char * file_name)194 msgdomain_write_desktop (message_list_ty *mlp,
195 const char *canon_encoding,
196 const char *locale_name,
197 const char *template_file_name,
198 hash_table *keywords,
199 const char *file_name)
200 {
201 msgfmt_operand_ty operand;
202 msgfmt_operand_list_ty operands;
203
204 /* Convert the messages to Unicode. */
205 iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
206
207 /* Support for "reproducible builds": Delete information that may vary
208 between builds in the same conditions. */
209 message_list_delete_header_field (mlp, "POT-Creation-Date:");
210
211 /* Create a single-element operands and run the bulk operation on it. */
212 operand.language = (char *) locale_name;
213 operand.mlp = mlp;
214 operands.nitems = 1;
215 operands.items = &operand;
216
217 return msgdomain_write_desktop_bulk (&operands,
218 template_file_name,
219 keywords,
220 file_name);
221 }
222