No empty .Rs/.Re
[netbsd-mini2440.git] / gnu / dist / gettext / gettext-tools / src / write-stringtable.c
blobf99d2d1eb881bf128debfb18721a75d7b177d5a5
1 /* Writing NeXTstep/GNUstep .strings files.
2 Copyright (C) 2003 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
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 2, or (at your option)
8 any later version.
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.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
23 /* Specification. */
24 #include "write-stringtable.h"
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
31 #include "message.h"
32 #include "msgl-ascii.h"
33 #include "msgl-iconv.h"
34 #include "po-charset.h"
35 #include "strstr.h"
36 #include "write-po.h"
38 /* The format of NeXTstep/GNUstep .strings files is documented in
39 gnustep-base-1.8.0/Tools/make_strings/Using.txt
40 and in the comments of method propertyListFromStringsFileFormat in
41 gnustep-base-1.8.0/Source/NSString.m
42 In summary, it's a Objective-C like file with pseudo-assignments of the form
43 "key" = "value";
44 where the key is the msgid and the value is the msgstr.
47 /* Handling of comments: We copy all comments from the PO file to the
48 .strings file. This is not really needed; it's a service for translators
49 who don't like PO files and prefer to maintain the .strings file. */
51 /* Since the interpretation of text files in GNUstep depends on the locale's
52 encoding if they don't have a BOM, we choose one of three encodings with
53 a BOM: UCS-2BE, UCS-2LE, UTF-8. Since the first two of these don't cope
54 with all of Unicode and we don't know whether GNUstep will switch to
55 UTF-16 instead of UCS-2, we use UTF-8 with BOM. BOMs are bad because they
56 get in the way when concatenating files, but here we have no choice. */
58 /* Writes a key or value to the file, without newline. */
59 static void
60 write_escaped_string (FILE *fp, const char *str)
62 const char *str_limit = str + strlen (str);
64 putc ('"', fp);
65 while (str < str_limit)
67 unsigned char c = (unsigned char) *str++;
69 if (c == '\t')
71 putc ('\\', fp);
72 putc ('t', fp);
74 else if (c == '\n')
76 putc ('\\', fp);
77 putc ('n', fp);
79 else if (c == '\r')
81 putc ('\\', fp);
82 putc ('r', fp);
84 else if (c == '\f')
86 putc ('\\', fp);
87 putc ('f', fp);
89 else if (c == '\\' || c == '"')
91 putc ('\\', fp);
92 putc (c, fp);
94 else
95 putc (c, fp);
97 putc ('"', fp);
100 /* Writes a message to the file. */
101 static void
102 write_message (FILE *fp, const message_ty *mp, size_t page_width, bool debug)
104 /* Print translator comment if available. */
105 if (mp->comment != NULL)
107 size_t j;
109 for (j = 0; j < mp->comment->nitems; ++j)
111 const char *s = mp->comment->item[j];
113 /* Test whether it is safe to output the comment in C style, or
114 whether we need C++ style for it. */
115 if (strstr (s, "*/") == NULL)
117 fputs ("/*", fp);
118 if (*s != '\0' && *s != '\n' && *s != ' ')
119 putc (' ', fp);
120 fputs (s, fp);
121 fputs (" */\n", fp);
123 else
126 const char *e;
127 fputs ("//", fp);
128 if (*s != '\0' && *s != '\n' && *s != ' ')
129 putc (' ', fp);
130 e = strchr (s, '\n');
131 if (e == NULL)
133 fputs (s, fp);
134 s = NULL;
136 else
138 fwrite (s, 1, e - s, fp);
139 s = e + 1;
141 putc ('\n', fp);
143 while (s != NULL);
147 /* Print xgettext extracted comments. */
148 if (mp->comment_dot != NULL)
150 size_t j;
152 for (j = 0; j < mp->comment_dot->nitems; ++j)
154 const char *s = mp->comment_dot->item[j];
156 /* Test whether it is safe to output the comment in C style, or
157 whether we need C++ style for it. */
158 if (strstr (s, "*/") == NULL)
160 fputs ("/* Comment: ", fp);
161 fputs (s, fp);
162 fputs (" */\n", fp);
164 else
166 bool first = true;
169 const char *e;
170 fputs ("//", fp);
171 if (first || (*s != '\0' && *s != '\n' && *s != ' '))
172 putc (' ', fp);
173 if (first)
174 fputs ("Comment: ", fp);
175 e = strchr (s, '\n');
176 if (e == NULL)
178 fputs (s, fp);
179 s = NULL;
181 else
183 fwrite (s, 1, e - s, fp);
184 s = e + 1;
186 putc ('\n', fp);
187 first = false;
189 while (s != NULL);
194 /* Print the file position comments. */
195 if (mp->filepos_count != 0)
197 size_t j;
199 for (j = 0; j < mp->filepos_count; ++j)
201 lex_pos_ty *pp = &mp->filepos[j];
202 char *cp = pp->file_name;
203 while (cp[0] == '.' && cp[1] == '/')
204 cp += 2;
205 fprintf (fp, "/* File: %s:%ld */\n", cp, (long) pp->line_number);
209 /* Print flag information in special comment. */
210 if (mp->is_fuzzy || mp->msgstr[0] == '\0')
211 fputs ("/* Flag: untranslated */\n", fp);
212 if (mp->obsolete)
213 fputs ("/* Flag: unmatched */\n", fp);
215 size_t i;
216 for (i = 0; i < NFORMATS; i++)
217 if (significant_format_p (mp->is_format[i]))
219 fputs ("/* Flag:", fp);
220 fputs (make_format_description_string (mp->is_format[i],
221 format_language[i], debug),
222 fp);
223 fputs (" */\n", fp);
227 /* Now write the untranslated string and the translated string. */
228 write_escaped_string (fp, mp->msgid);
229 fputs (" = ", fp);
230 if (mp->msgstr[0] != '\0')
232 if (mp->is_fuzzy)
234 /* Output the msgid as value, so that at runtime the untranslated
235 string is returned. */
236 write_escaped_string (fp, mp->msgid);
238 /* Output the msgstr as a comment, so that at runtime
239 propertyListFromStringsFileFormat ignores it. */
240 if (strstr (mp->msgstr, "*/") == NULL)
242 fputs (" /* = ", fp);
243 write_escaped_string (fp, mp->msgstr);
244 fputs (" */", fp);
246 else
248 fputs ("; // = ", fp);
249 write_escaped_string (fp, mp->msgstr);
252 else
253 write_escaped_string (fp, mp->msgstr);
255 else
257 /* Output the msgid as value, so that at runtime the untranslated
258 string is returned. */
259 write_escaped_string (fp, mp->msgid);
261 putc (';', fp);
263 putc ('\n', fp);
266 /* Writes an entire message list to the file. */
267 static void
268 write_stringtable (FILE *fp, message_list_ty *mlp, const char *canon_encoding,
269 size_t page_width, bool debug)
271 bool blank_line;
272 size_t j;
274 /* Convert the messages to Unicode. */
275 iconv_message_list (mlp, canon_encoding, po_charset_utf8, NULL);
277 /* Output the BOM. */
278 if (!is_ascii_message_list (mlp))
279 fputs ("\xef\xbb\xbf", fp);
281 /* Loop through the messages. */
282 blank_line = false;
283 for (j = 0; j < mlp->nitems; ++j)
285 const message_ty *mp = mlp->item[j];
287 if (mp->msgid_plural == NULL)
289 if (blank_line)
290 putc ('\n', fp);
292 write_message (fp, mp, page_width, debug);
294 blank_line = true;
299 /* Output the contents of a PO file in .strings syntax. */
300 void
301 msgdomain_list_print_stringtable (msgdomain_list_ty *mdlp, FILE *fp,
302 size_t page_width, bool debug)
304 message_list_ty *mlp;
306 if (mdlp->nitems == 1)
307 mlp = mdlp->item[0]->messages;
308 else
309 mlp = message_list_alloc (false);
310 write_stringtable (fp, mlp, mdlp->encoding, page_width, debug);