1*946379e7Schristos /* GNU gettext - internationalization aids
2*946379e7Schristos Copyright (C) 1995-1998, 2000-2006 Free Software Foundation, Inc.
3*946379e7Schristos
4*946379e7Schristos This file was written by Peter Miller <millerp@canb.auug.org.au>
5*946379e7Schristos
6*946379e7Schristos This program is free software; you can redistribute it and/or modify
7*946379e7Schristos it under the terms of the GNU General Public License as published by
8*946379e7Schristos the Free Software Foundation; either version 2, or (at your option)
9*946379e7Schristos any later version.
10*946379e7Schristos
11*946379e7Schristos This program is distributed in the hope that it will be useful,
12*946379e7Schristos but WITHOUT ANY WARRANTY; without even the implied warranty of
13*946379e7Schristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14*946379e7Schristos GNU General Public License for more details.
15*946379e7Schristos
16*946379e7Schristos You should have received a copy of the GNU General Public License
17*946379e7Schristos along with this program; if not, write to the Free Software Foundation,
18*946379e7Schristos Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
19*946379e7Schristos
20*946379e7Schristos #ifdef HAVE_CONFIG_H
21*946379e7Schristos # include <config.h>
22*946379e7Schristos #endif
23*946379e7Schristos #include <alloca.h>
24*946379e7Schristos
25*946379e7Schristos /* Specification. */
26*946379e7Schristos #include "write-po.h"
27*946379e7Schristos
28*946379e7Schristos #include <errno.h>
29*946379e7Schristos #include <limits.h>
30*946379e7Schristos #include <stdio.h>
31*946379e7Schristos #include <stdlib.h>
32*946379e7Schristos #include <string.h>
33*946379e7Schristos
34*946379e7Schristos #if HAVE_ICONV
35*946379e7Schristos # include <iconv.h>
36*946379e7Schristos #endif
37*946379e7Schristos
38*946379e7Schristos #include "c-ctype.h"
39*946379e7Schristos #include "po-charset.h"
40*946379e7Schristos #include "linebreak.h"
41*946379e7Schristos #include "msgl-ascii.h"
42*946379e7Schristos #include "write-properties.h"
43*946379e7Schristos #include "write-stringtable.h"
44*946379e7Schristos #include "xalloc.h"
45*946379e7Schristos #include "xallocsa.h"
46*946379e7Schristos #include "c-strstr.h"
47*946379e7Schristos #include "xvasprintf.h"
48*946379e7Schristos #include "po-xerror.h"
49*946379e7Schristos #include "gettext.h"
50*946379e7Schristos
51*946379e7Schristos /* Our regular abbreviation. */
52*946379e7Schristos #define _(str) gettext (str)
53*946379e7Schristos
54*946379e7Schristos #if HAVE_DECL_PUTC_UNLOCKED
55*946379e7Schristos # undef putc
56*946379e7Schristos # define putc putc_unlocked
57*946379e7Schristos #endif
58*946379e7Schristos
59*946379e7Schristos
60*946379e7Schristos /* =================== Putting together a #, flags line. =================== */
61*946379e7Schristos
62*946379e7Schristos
63*946379e7Schristos /* Convert IS_FORMAT in the context of programming language LANG to a flag
64*946379e7Schristos string for use in #, flags. */
65*946379e7Schristos
66*946379e7Schristos const char *
make_format_description_string(enum is_format is_format,const char * lang,bool debug)67*946379e7Schristos make_format_description_string (enum is_format is_format, const char *lang,
68*946379e7Schristos bool debug)
69*946379e7Schristos {
70*946379e7Schristos static char result[100];
71*946379e7Schristos
72*946379e7Schristos switch (is_format)
73*946379e7Schristos {
74*946379e7Schristos case possible:
75*946379e7Schristos if (debug)
76*946379e7Schristos {
77*946379e7Schristos sprintf (result, " possible-%s-format", lang);
78*946379e7Schristos break;
79*946379e7Schristos }
80*946379e7Schristos /* FALLTHROUGH */
81*946379e7Schristos case yes_according_to_context:
82*946379e7Schristos case yes:
83*946379e7Schristos sprintf (result, " %s-format", lang);
84*946379e7Schristos break;
85*946379e7Schristos case no:
86*946379e7Schristos sprintf (result, " no-%s-format", lang);
87*946379e7Schristos break;
88*946379e7Schristos default:
89*946379e7Schristos /* The others have already been filtered out by significant_format_p. */
90*946379e7Schristos abort ();
91*946379e7Schristos }
92*946379e7Schristos
93*946379e7Schristos return result;
94*946379e7Schristos }
95*946379e7Schristos
96*946379e7Schristos
97*946379e7Schristos /* Return true if IS_FORMAT is worth mentioning in a #, flags list. */
98*946379e7Schristos
99*946379e7Schristos bool
significant_format_p(enum is_format is_format)100*946379e7Schristos significant_format_p (enum is_format is_format)
101*946379e7Schristos {
102*946379e7Schristos return is_format != undecided && is_format != impossible;
103*946379e7Schristos }
104*946379e7Schristos
105*946379e7Schristos
106*946379e7Schristos /* Return true if one of IS_FORMAT is worth mentioning in a #, flags list. */
107*946379e7Schristos
108*946379e7Schristos static bool
has_significant_format_p(const enum is_format is_format[NFORMATS])109*946379e7Schristos has_significant_format_p (const enum is_format is_format[NFORMATS])
110*946379e7Schristos {
111*946379e7Schristos size_t i;
112*946379e7Schristos
113*946379e7Schristos for (i = 0; i < NFORMATS; i++)
114*946379e7Schristos if (significant_format_p (is_format[i]))
115*946379e7Schristos return true;
116*946379e7Schristos return false;
117*946379e7Schristos }
118*946379e7Schristos
119*946379e7Schristos
120*946379e7Schristos /* Convert a wrapping flag DO_WRAP to a string for use in #, flags. */
121*946379e7Schristos
122*946379e7Schristos static const char *
make_c_width_description_string(enum is_wrap do_wrap)123*946379e7Schristos make_c_width_description_string (enum is_wrap do_wrap)
124*946379e7Schristos {
125*946379e7Schristos const char *result = NULL;
126*946379e7Schristos
127*946379e7Schristos switch (do_wrap)
128*946379e7Schristos {
129*946379e7Schristos case yes:
130*946379e7Schristos result = " wrap";
131*946379e7Schristos break;
132*946379e7Schristos case no:
133*946379e7Schristos result = " no-wrap";
134*946379e7Schristos break;
135*946379e7Schristos default:
136*946379e7Schristos abort ();
137*946379e7Schristos }
138*946379e7Schristos
139*946379e7Schristos return result;
140*946379e7Schristos }
141*946379e7Schristos
142*946379e7Schristos
143*946379e7Schristos /* ================ Output parts of a message, as comments. ================ */
144*946379e7Schristos
145*946379e7Schristos
146*946379e7Schristos /* Output mp->comment as a set of comment lines. */
147*946379e7Schristos
148*946379e7Schristos void
message_print_comment(const message_ty * mp,FILE * fp)149*946379e7Schristos message_print_comment (const message_ty *mp, FILE *fp)
150*946379e7Schristos {
151*946379e7Schristos if (mp->comment != NULL)
152*946379e7Schristos {
153*946379e7Schristos size_t j;
154*946379e7Schristos
155*946379e7Schristos for (j = 0; j < mp->comment->nitems; ++j)
156*946379e7Schristos {
157*946379e7Schristos const char *s = mp->comment->item[j];
158*946379e7Schristos do
159*946379e7Schristos {
160*946379e7Schristos const char *e;
161*946379e7Schristos putc ('#', fp);
162*946379e7Schristos if (*s != '\0' && *s != ' ')
163*946379e7Schristos putc (' ', fp);
164*946379e7Schristos e = strchr (s, '\n');
165*946379e7Schristos if (e == NULL)
166*946379e7Schristos {
167*946379e7Schristos fputs (s, fp);
168*946379e7Schristos s = NULL;
169*946379e7Schristos }
170*946379e7Schristos else
171*946379e7Schristos {
172*946379e7Schristos fwrite (s, 1, e - s, fp);
173*946379e7Schristos s = e + 1;
174*946379e7Schristos }
175*946379e7Schristos putc ('\n', fp);
176*946379e7Schristos }
177*946379e7Schristos while (s != NULL);
178*946379e7Schristos }
179*946379e7Schristos }
180*946379e7Schristos }
181*946379e7Schristos
182*946379e7Schristos
183*946379e7Schristos /* Output mp->comment_dot as a set of comment lines. */
184*946379e7Schristos
185*946379e7Schristos void
message_print_comment_dot(const message_ty * mp,FILE * fp)186*946379e7Schristos message_print_comment_dot (const message_ty *mp, FILE *fp)
187*946379e7Schristos {
188*946379e7Schristos if (mp->comment_dot != NULL)
189*946379e7Schristos {
190*946379e7Schristos size_t j;
191*946379e7Schristos
192*946379e7Schristos for (j = 0; j < mp->comment_dot->nitems; ++j)
193*946379e7Schristos {
194*946379e7Schristos const char *s = mp->comment_dot->item[j];
195*946379e7Schristos putc ('#', fp);
196*946379e7Schristos putc ('.', fp);
197*946379e7Schristos if (*s != '\0' && *s != ' ')
198*946379e7Schristos putc (' ', fp);
199*946379e7Schristos fputs (s, fp);
200*946379e7Schristos putc ('\n', fp);
201*946379e7Schristos }
202*946379e7Schristos }
203*946379e7Schristos }
204*946379e7Schristos
205*946379e7Schristos
206*946379e7Schristos /* Output mp->filepos as a set of comment lines. */
207*946379e7Schristos
208*946379e7Schristos void
message_print_comment_filepos(const message_ty * mp,FILE * fp,bool uniforum,size_t page_width)209*946379e7Schristos message_print_comment_filepos (const message_ty *mp, FILE *fp,
210*946379e7Schristos bool uniforum, size_t page_width)
211*946379e7Schristos {
212*946379e7Schristos if (mp->filepos_count != 0)
213*946379e7Schristos {
214*946379e7Schristos if (uniforum)
215*946379e7Schristos {
216*946379e7Schristos size_t j;
217*946379e7Schristos
218*946379e7Schristos for (j = 0; j < mp->filepos_count; ++j)
219*946379e7Schristos {
220*946379e7Schristos lex_pos_ty *pp = &mp->filepos[j];
221*946379e7Schristos char *cp = pp->file_name;
222*946379e7Schristos while (cp[0] == '.' && cp[1] == '/')
223*946379e7Schristos cp += 2;
224*946379e7Schristos /* There are two Sun formats to choose from: SunOS and
225*946379e7Schristos Solaris. Use the Solaris form here. */
226*946379e7Schristos fprintf (fp, "# File: %s, line: %ld\n",
227*946379e7Schristos cp, (long) pp->line_number);
228*946379e7Schristos }
229*946379e7Schristos }
230*946379e7Schristos else
231*946379e7Schristos {
232*946379e7Schristos size_t column;
233*946379e7Schristos size_t j;
234*946379e7Schristos
235*946379e7Schristos fputs ("#:", fp);
236*946379e7Schristos column = 2;
237*946379e7Schristos for (j = 0; j < mp->filepos_count; ++j)
238*946379e7Schristos {
239*946379e7Schristos lex_pos_ty *pp;
240*946379e7Schristos char buffer[21];
241*946379e7Schristos char *cp;
242*946379e7Schristos size_t len;
243*946379e7Schristos
244*946379e7Schristos pp = &mp->filepos[j];
245*946379e7Schristos cp = pp->file_name;
246*946379e7Schristos while (cp[0] == '.' && cp[1] == '/')
247*946379e7Schristos cp += 2;
248*946379e7Schristos /* Some xgettext input formats, like RST, lack line numbers. */
249*946379e7Schristos if (pp->line_number == (size_t)(-1))
250*946379e7Schristos buffer[0] = '\0';
251*946379e7Schristos else
252*946379e7Schristos sprintf (buffer, ":%ld", (long) pp->line_number);
253*946379e7Schristos len = strlen (cp) + strlen (buffer) + 1;
254*946379e7Schristos if (column > 2 && column + len >= page_width)
255*946379e7Schristos {
256*946379e7Schristos fputs ("\n#:", fp);
257*946379e7Schristos column = 2;
258*946379e7Schristos }
259*946379e7Schristos fprintf (fp, " %s%s", cp, buffer);
260*946379e7Schristos column += len;
261*946379e7Schristos }
262*946379e7Schristos putc ('\n', fp);
263*946379e7Schristos }
264*946379e7Schristos }
265*946379e7Schristos }
266*946379e7Schristos
267*946379e7Schristos
268*946379e7Schristos /* Output mp->is_fuzzy, mp->is_format, mp->do_wrap as a comment line. */
269*946379e7Schristos
270*946379e7Schristos void
message_print_comment_flags(const message_ty * mp,FILE * fp,bool debug)271*946379e7Schristos message_print_comment_flags (const message_ty *mp, FILE *fp, bool debug)
272*946379e7Schristos {
273*946379e7Schristos if ((mp->is_fuzzy && mp->msgstr[0] != '\0')
274*946379e7Schristos || has_significant_format_p (mp->is_format)
275*946379e7Schristos || mp->do_wrap == no)
276*946379e7Schristos {
277*946379e7Schristos bool first_flag = true;
278*946379e7Schristos size_t i;
279*946379e7Schristos
280*946379e7Schristos putc ('#', fp);
281*946379e7Schristos putc (',', fp);
282*946379e7Schristos
283*946379e7Schristos /* We don't print the fuzzy flag if the msgstr is empty. This
284*946379e7Schristos might be introduced by the user but we want to normalize the
285*946379e7Schristos output. */
286*946379e7Schristos if (mp->is_fuzzy && mp->msgstr[0] != '\0')
287*946379e7Schristos {
288*946379e7Schristos fputs (" fuzzy", fp);
289*946379e7Schristos first_flag = false;
290*946379e7Schristos }
291*946379e7Schristos
292*946379e7Schristos for (i = 0; i < NFORMATS; i++)
293*946379e7Schristos if (significant_format_p (mp->is_format[i]))
294*946379e7Schristos {
295*946379e7Schristos if (!first_flag)
296*946379e7Schristos putc (',', fp);
297*946379e7Schristos
298*946379e7Schristos fputs (make_format_description_string (mp->is_format[i],
299*946379e7Schristos format_language[i], debug),
300*946379e7Schristos fp);
301*946379e7Schristos first_flag = false;
302*946379e7Schristos }
303*946379e7Schristos
304*946379e7Schristos if (mp->do_wrap == no)
305*946379e7Schristos {
306*946379e7Schristos if (!first_flag)
307*946379e7Schristos putc (',', fp);
308*946379e7Schristos
309*946379e7Schristos fputs (make_c_width_description_string (mp->do_wrap), fp);
310*946379e7Schristos first_flag = false;
311*946379e7Schristos }
312*946379e7Schristos
313*946379e7Schristos putc ('\n', fp);
314*946379e7Schristos }
315*946379e7Schristos }
316*946379e7Schristos
317*946379e7Schristos
318*946379e7Schristos /* ========= Some parameters for use by 'msgdomain_list_print_po'. ========= */
319*946379e7Schristos
320*946379e7Schristos
321*946379e7Schristos /* This variable controls the extent to which the page width applies.
322*946379e7Schristos True means it applies to message strings and file reference lines.
323*946379e7Schristos False means it applies to file reference lines only. */
324*946379e7Schristos static bool wrap_strings = true;
325*946379e7Schristos
326*946379e7Schristos void
message_page_width_ignore()327*946379e7Schristos message_page_width_ignore ()
328*946379e7Schristos {
329*946379e7Schristos wrap_strings = false;
330*946379e7Schristos }
331*946379e7Schristos
332*946379e7Schristos
333*946379e7Schristos /* These three variables control the output style of the message_print
334*946379e7Schristos function. Interface functions for them are to be used. */
335*946379e7Schristos static bool indent = false;
336*946379e7Schristos static bool uniforum = false;
337*946379e7Schristos static bool escape = false;
338*946379e7Schristos
339*946379e7Schristos void
message_print_style_indent()340*946379e7Schristos message_print_style_indent ()
341*946379e7Schristos {
342*946379e7Schristos indent = true;
343*946379e7Schristos }
344*946379e7Schristos
345*946379e7Schristos void
message_print_style_uniforum()346*946379e7Schristos message_print_style_uniforum ()
347*946379e7Schristos {
348*946379e7Schristos uniforum = true;
349*946379e7Schristos }
350*946379e7Schristos
351*946379e7Schristos void
message_print_style_escape(bool flag)352*946379e7Schristos message_print_style_escape (bool flag)
353*946379e7Schristos {
354*946379e7Schristos escape = flag;
355*946379e7Schristos }
356*946379e7Schristos
357*946379e7Schristos
358*946379e7Schristos /* =============== msgdomain_list_print_po() and subroutines. =============== */
359*946379e7Schristos
360*946379e7Schristos
361*946379e7Schristos /* A version of memcpy optimized for the case n <= 1. */
362*946379e7Schristos static inline void
memcpy_small(void * dst,const void * src,size_t n)363*946379e7Schristos memcpy_small (void *dst, const void *src, size_t n)
364*946379e7Schristos {
365*946379e7Schristos if (n > 0)
366*946379e7Schristos {
367*946379e7Schristos char *q = (char *) dst;
368*946379e7Schristos const char *p = (const char *) src;
369*946379e7Schristos
370*946379e7Schristos *q = *p;
371*946379e7Schristos if (--n > 0)
372*946379e7Schristos do *++q = *++p; while (--n > 0);
373*946379e7Schristos }
374*946379e7Schristos }
375*946379e7Schristos
376*946379e7Schristos
377*946379e7Schristos static void
wrap(const message_ty * mp,FILE * fp,const char * line_prefix,int extra_indent,const char * name,const char * value,enum is_wrap do_wrap,size_t page_width,const char * charset)378*946379e7Schristos wrap (const message_ty *mp, FILE *fp, const char *line_prefix, int extra_indent,
379*946379e7Schristos const char *name, const char *value,
380*946379e7Schristos enum is_wrap do_wrap, size_t page_width,
381*946379e7Schristos const char *charset)
382*946379e7Schristos {
383*946379e7Schristos const char *canon_charset;
384*946379e7Schristos const char *s;
385*946379e7Schristos bool first_line;
386*946379e7Schristos #if HAVE_ICONV
387*946379e7Schristos const char *envval;
388*946379e7Schristos iconv_t conv;
389*946379e7Schristos #endif
390*946379e7Schristos bool weird_cjk;
391*946379e7Schristos
392*946379e7Schristos canon_charset = po_charset_canonicalize (charset);
393*946379e7Schristos
394*946379e7Schristos #if HAVE_ICONV
395*946379e7Schristos /* The old Solaris/openwin msgfmt and GNU msgfmt <= 0.10.35 don't know
396*946379e7Schristos about multibyte encodings, and require a spurious backslash after
397*946379e7Schristos every multibyte character whose last byte is 0x5C. Some programs,
398*946379e7Schristos like vim, distribute PO files in this broken format. It is important
399*946379e7Schristos for such programs that GNU msgmerge continues to support this old
400*946379e7Schristos PO file format when the Makefile requests it. */
401*946379e7Schristos envval = getenv ("OLD_PO_FILE_OUTPUT");
402*946379e7Schristos if (envval != NULL && *envval != '\0')
403*946379e7Schristos /* Write a PO file in old format, with extraneous backslashes. */
404*946379e7Schristos conv = (iconv_t)(-1);
405*946379e7Schristos else
406*946379e7Schristos if (canon_charset == NULL)
407*946379e7Schristos /* Invalid PO file encoding. */
408*946379e7Schristos conv = (iconv_t)(-1);
409*946379e7Schristos else
410*946379e7Schristos /* Avoid glibc-2.1 bug with EUC-KR. */
411*946379e7Schristos # if (__GLIBC__ - 0 == 2 && __GLIBC_MINOR__ - 0 <= 1) && !defined _LIBICONV_VERSION
412*946379e7Schristos if (strcmp (canon_charset, "EUC-KR") == 0)
413*946379e7Schristos conv = (iconv_t)(-1);
414*946379e7Schristos else
415*946379e7Schristos # endif
416*946379e7Schristos /* Avoid Solaris 2.9 bug with GB2312, EUC-TW, BIG5, BIG5-HKSCS, GBK,
417*946379e7Schristos GB18030. */
418*946379e7Schristos # if defined __sun && !defined _LIBICONV_VERSION
419*946379e7Schristos if ( strcmp (canon_charset, "GB2312") == 0
420*946379e7Schristos || strcmp (canon_charset, "EUC-TW") == 0
421*946379e7Schristos || strcmp (canon_charset, "BIG5") == 0
422*946379e7Schristos || strcmp (canon_charset, "BIG5-HKSCS") == 0
423*946379e7Schristos || strcmp (canon_charset, "GBK") == 0
424*946379e7Schristos || strcmp (canon_charset, "GB18030") == 0)
425*946379e7Schristos conv = (iconv_t)(-1);
426*946379e7Schristos else
427*946379e7Schristos # endif
428*946379e7Schristos /* Use iconv() to parse multibyte characters. */
429*946379e7Schristos conv = iconv_open ("UTF-8", canon_charset);
430*946379e7Schristos
431*946379e7Schristos if (conv != (iconv_t)(-1))
432*946379e7Schristos weird_cjk = false;
433*946379e7Schristos else
434*946379e7Schristos #endif
435*946379e7Schristos if (canon_charset == NULL)
436*946379e7Schristos weird_cjk = false;
437*946379e7Schristos else
438*946379e7Schristos weird_cjk = po_is_charset_weird_cjk (canon_charset);
439*946379e7Schristos
440*946379e7Schristos if (canon_charset == NULL)
441*946379e7Schristos canon_charset = po_charset_ascii;
442*946379e7Schristos
443*946379e7Schristos /* Loop over the '\n' delimited portions of value. */
444*946379e7Schristos s = value;
445*946379e7Schristos first_line = true;
446*946379e7Schristos do
447*946379e7Schristos {
448*946379e7Schristos /* The usual escapes, as defined by the ANSI C Standard. */
449*946379e7Schristos # define is_escape(c) \
450*946379e7Schristos ((c) == '\a' || (c) == '\b' || (c) == '\f' || (c) == '\n' \
451*946379e7Schristos || (c) == '\r' || (c) == '\t' || (c) == '\v')
452*946379e7Schristos
453*946379e7Schristos const char *es;
454*946379e7Schristos const char *ep;
455*946379e7Schristos size_t portion_len;
456*946379e7Schristos char *portion;
457*946379e7Schristos char *overrides;
458*946379e7Schristos char *linebreaks;
459*946379e7Schristos char *pp;
460*946379e7Schristos char *op;
461*946379e7Schristos int startcol, startcol_after_break, width;
462*946379e7Schristos size_t i;
463*946379e7Schristos
464*946379e7Schristos for (es = s; *es != '\0'; )
465*946379e7Schristos if (*es++ == '\n')
466*946379e7Schristos break;
467*946379e7Schristos
468*946379e7Schristos /* Expand escape sequences in each portion. */
469*946379e7Schristos for (ep = s, portion_len = 0; ep < es; ep++)
470*946379e7Schristos {
471*946379e7Schristos char c = *ep;
472*946379e7Schristos if (is_escape (c))
473*946379e7Schristos portion_len += 2;
474*946379e7Schristos else if (escape && !c_isprint ((unsigned char) c))
475*946379e7Schristos portion_len += 4;
476*946379e7Schristos else if (c == '\\' || c == '"')
477*946379e7Schristos portion_len += 2;
478*946379e7Schristos else
479*946379e7Schristos {
480*946379e7Schristos #if HAVE_ICONV
481*946379e7Schristos if (conv != (iconv_t)(-1))
482*946379e7Schristos {
483*946379e7Schristos /* Skip over a complete multi-byte character. Don't
484*946379e7Schristos interpret the second byte of a multi-byte character as
485*946379e7Schristos ASCII. This is needed for the BIG5, BIG5-HKSCS, GBK,
486*946379e7Schristos GB18030, SHIFT_JIS, JOHAB encodings. */
487*946379e7Schristos char scratchbuf[64];
488*946379e7Schristos const char *inptr = ep;
489*946379e7Schristos size_t insize;
490*946379e7Schristos char *outptr = &scratchbuf[0];
491*946379e7Schristos size_t outsize = sizeof (scratchbuf);
492*946379e7Schristos size_t res;
493*946379e7Schristos
494*946379e7Schristos res = (size_t)(-1);
495*946379e7Schristos for (insize = 1; inptr + insize <= es; insize++)
496*946379e7Schristos {
497*946379e7Schristos res = iconv (conv,
498*946379e7Schristos (ICONV_CONST char **) &inptr, &insize,
499*946379e7Schristos &outptr, &outsize);
500*946379e7Schristos if (!(res == (size_t)(-1) && errno == EINVAL))
501*946379e7Schristos break;
502*946379e7Schristos /* We expect that no input bytes have been consumed
503*946379e7Schristos so far. */
504*946379e7Schristos if (inptr != ep)
505*946379e7Schristos abort ();
506*946379e7Schristos }
507*946379e7Schristos if (res == (size_t)(-1))
508*946379e7Schristos {
509*946379e7Schristos if (errno == EILSEQ)
510*946379e7Schristos {
511*946379e7Schristos po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0, false,
512*946379e7Schristos _("invalid multibyte sequence"));
513*946379e7Schristos continue;
514*946379e7Schristos }
515*946379e7Schristos else
516*946379e7Schristos abort ();
517*946379e7Schristos }
518*946379e7Schristos insize = inptr - ep;
519*946379e7Schristos portion_len += insize;
520*946379e7Schristos ep += insize - 1;
521*946379e7Schristos }
522*946379e7Schristos else
523*946379e7Schristos #endif
524*946379e7Schristos {
525*946379e7Schristos if (weird_cjk
526*946379e7Schristos /* Special handling of encodings with CJK structure. */
527*946379e7Schristos && ep + 2 <= es
528*946379e7Schristos && (unsigned char) ep[0] >= 0x80
529*946379e7Schristos && (unsigned char) ep[1] >= 0x30)
530*946379e7Schristos {
531*946379e7Schristos portion_len += 2;
532*946379e7Schristos ep += 1;
533*946379e7Schristos }
534*946379e7Schristos else
535*946379e7Schristos portion_len += 1;
536*946379e7Schristos }
537*946379e7Schristos }
538*946379e7Schristos }
539*946379e7Schristos portion = (char *) xmalloc (portion_len);
540*946379e7Schristos overrides = (char *) xmalloc (portion_len);
541*946379e7Schristos memset (overrides, UC_BREAK_UNDEFINED, portion_len);
542*946379e7Schristos for (ep = s, pp = portion, op = overrides; ep < es; ep++)
543*946379e7Schristos {
544*946379e7Schristos char c = *ep;
545*946379e7Schristos if (is_escape (c))
546*946379e7Schristos {
547*946379e7Schristos switch (c)
548*946379e7Schristos {
549*946379e7Schristos case '\a': c = 'a'; break;
550*946379e7Schristos case '\b': c = 'b'; break;
551*946379e7Schristos case '\f': c = 'f'; break;
552*946379e7Schristos case '\n': c = 'n'; break;
553*946379e7Schristos case '\r': c = 'r'; break;
554*946379e7Schristos case '\t': c = 't'; break;
555*946379e7Schristos case '\v': c = 'v'; break;
556*946379e7Schristos default: abort ();
557*946379e7Schristos }
558*946379e7Schristos *pp++ = '\\';
559*946379e7Schristos *pp++ = c;
560*946379e7Schristos op++;
561*946379e7Schristos *op++ = UC_BREAK_PROHIBITED;
562*946379e7Schristos /* We warn about any use of escape sequences beside
563*946379e7Schristos '\n' and '\t'. */
564*946379e7Schristos if (c != 'n' && c != 't')
565*946379e7Schristos {
566*946379e7Schristos char *error_message =
567*946379e7Schristos xasprintf (_("\
568*946379e7Schristos internationalized messages should not contain the `\\%c' escape sequence"),
569*946379e7Schristos c);
570*946379e7Schristos po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0, false,
571*946379e7Schristos error_message);
572*946379e7Schristos free (error_message);
573*946379e7Schristos }
574*946379e7Schristos }
575*946379e7Schristos else if (escape && !c_isprint ((unsigned char) c))
576*946379e7Schristos {
577*946379e7Schristos *pp++ = '\\';
578*946379e7Schristos *pp++ = '0' + (((unsigned char) c >> 6) & 7);
579*946379e7Schristos *pp++ = '0' + (((unsigned char) c >> 3) & 7);
580*946379e7Schristos *pp++ = '0' + ((unsigned char) c & 7);
581*946379e7Schristos op++;
582*946379e7Schristos *op++ = UC_BREAK_PROHIBITED;
583*946379e7Schristos *op++ = UC_BREAK_PROHIBITED;
584*946379e7Schristos *op++ = UC_BREAK_PROHIBITED;
585*946379e7Schristos }
586*946379e7Schristos else if (c == '\\' || c == '"')
587*946379e7Schristos {
588*946379e7Schristos *pp++ = '\\';
589*946379e7Schristos *pp++ = c;
590*946379e7Schristos op++;
591*946379e7Schristos *op++ = UC_BREAK_PROHIBITED;
592*946379e7Schristos }
593*946379e7Schristos else
594*946379e7Schristos {
595*946379e7Schristos #if HAVE_ICONV
596*946379e7Schristos if (conv != (iconv_t)(-1))
597*946379e7Schristos {
598*946379e7Schristos /* Copy a complete multi-byte character. Don't
599*946379e7Schristos interpret the second byte of a multi-byte character as
600*946379e7Schristos ASCII. This is needed for the BIG5, BIG5-HKSCS, GBK,
601*946379e7Schristos GB18030, SHIFT_JIS, JOHAB encodings. */
602*946379e7Schristos char scratchbuf[64];
603*946379e7Schristos const char *inptr = ep;
604*946379e7Schristos size_t insize;
605*946379e7Schristos char *outptr = &scratchbuf[0];
606*946379e7Schristos size_t outsize = sizeof (scratchbuf);
607*946379e7Schristos size_t res;
608*946379e7Schristos
609*946379e7Schristos res = (size_t)(-1);
610*946379e7Schristos for (insize = 1; inptr + insize <= es; insize++)
611*946379e7Schristos {
612*946379e7Schristos res = iconv (conv,
613*946379e7Schristos (ICONV_CONST char **) &inptr, &insize,
614*946379e7Schristos &outptr, &outsize);
615*946379e7Schristos if (!(res == (size_t)(-1) && errno == EINVAL))
616*946379e7Schristos break;
617*946379e7Schristos /* We expect that no input bytes have been consumed
618*946379e7Schristos so far. */
619*946379e7Schristos if (inptr != ep)
620*946379e7Schristos abort ();
621*946379e7Schristos }
622*946379e7Schristos if (res == (size_t)(-1))
623*946379e7Schristos {
624*946379e7Schristos if (errno == EILSEQ)
625*946379e7Schristos {
626*946379e7Schristos po_xerror (PO_SEVERITY_ERROR, mp, NULL, 0, 0,
627*946379e7Schristos false, _("invalid multibyte sequence"));
628*946379e7Schristos continue;
629*946379e7Schristos }
630*946379e7Schristos else
631*946379e7Schristos abort ();
632*946379e7Schristos }
633*946379e7Schristos insize = inptr - ep;
634*946379e7Schristos memcpy_small (pp, ep, insize);
635*946379e7Schristos pp += insize;
636*946379e7Schristos op += insize;
637*946379e7Schristos ep += insize - 1;
638*946379e7Schristos }
639*946379e7Schristos else
640*946379e7Schristos #endif
641*946379e7Schristos {
642*946379e7Schristos if (weird_cjk
643*946379e7Schristos /* Special handling of encodings with CJK structure. */
644*946379e7Schristos && ep + 2 <= es
645*946379e7Schristos && (unsigned char) c >= 0x80
646*946379e7Schristos && (unsigned char) ep[1] >= 0x30)
647*946379e7Schristos {
648*946379e7Schristos *pp++ = c;
649*946379e7Schristos ep += 1;
650*946379e7Schristos *pp++ = *ep;
651*946379e7Schristos op += 2;
652*946379e7Schristos }
653*946379e7Schristos else
654*946379e7Schristos {
655*946379e7Schristos *pp++ = c;
656*946379e7Schristos op++;
657*946379e7Schristos }
658*946379e7Schristos }
659*946379e7Schristos }
660*946379e7Schristos }
661*946379e7Schristos
662*946379e7Schristos /* Don't break immediately before the "\n" at the end. */
663*946379e7Schristos if (es > s && es[-1] == '\n')
664*946379e7Schristos overrides[portion_len - 2] = UC_BREAK_PROHIBITED;
665*946379e7Schristos
666*946379e7Schristos linebreaks = (char *) xmalloc (portion_len);
667*946379e7Schristos
668*946379e7Schristos /* Subsequent lines after a break are all indented.
669*946379e7Schristos See INDENT-S. */
670*946379e7Schristos startcol_after_break = (line_prefix ? strlen (line_prefix) : 0);
671*946379e7Schristos if (indent)
672*946379e7Schristos startcol_after_break = (startcol_after_break + extra_indent + 8) & ~7;
673*946379e7Schristos startcol_after_break++;
674*946379e7Schristos
675*946379e7Schristos /* The line width. Allow room for the closing quote character. */
676*946379e7Schristos width = (wrap_strings && do_wrap != no ? page_width : INT_MAX) - 1;
677*946379e7Schristos /* Adjust for indentation of subsequent lines. */
678*946379e7Schristos width -= startcol_after_break;
679*946379e7Schristos
680*946379e7Schristos recompute:
681*946379e7Schristos /* The line starts with different things depending on whether it
682*946379e7Schristos is the first line, and if we are using the indented style.
683*946379e7Schristos See INDENT-F. */
684*946379e7Schristos startcol = (line_prefix ? strlen (line_prefix) : 0);
685*946379e7Schristos if (first_line)
686*946379e7Schristos {
687*946379e7Schristos startcol += strlen (name);
688*946379e7Schristos if (indent)
689*946379e7Schristos startcol = (startcol + extra_indent + 8) & ~7;
690*946379e7Schristos else
691*946379e7Schristos startcol++;
692*946379e7Schristos }
693*946379e7Schristos else
694*946379e7Schristos {
695*946379e7Schristos if (indent)
696*946379e7Schristos startcol = (startcol + extra_indent + 8) & ~7;
697*946379e7Schristos }
698*946379e7Schristos /* Allow room for the opening quote character. */
699*946379e7Schristos startcol++;
700*946379e7Schristos /* Adjust for indentation of subsequent lines. */
701*946379e7Schristos startcol -= startcol_after_break;
702*946379e7Schristos
703*946379e7Schristos /* Do line breaking on the portion. */
704*946379e7Schristos mbs_width_linebreaks (portion, portion_len, width, startcol, 0,
705*946379e7Schristos overrides, canon_charset, linebreaks);
706*946379e7Schristos
707*946379e7Schristos /* If this is the first line, and we are not using the indented
708*946379e7Schristos style, and the line would wrap, then use an empty first line
709*946379e7Schristos and restart. */
710*946379e7Schristos if (first_line && !indent
711*946379e7Schristos && portion_len > 0
712*946379e7Schristos && (*es != '\0'
713*946379e7Schristos || startcol > width
714*946379e7Schristos || memchr (linebreaks, UC_BREAK_POSSIBLE, portion_len) != NULL))
715*946379e7Schristos {
716*946379e7Schristos if (line_prefix != NULL)
717*946379e7Schristos fputs (line_prefix, fp);
718*946379e7Schristos fputs (name, fp);
719*946379e7Schristos fputs (" \"\"\n", fp);
720*946379e7Schristos first_line = false;
721*946379e7Schristos /* Recompute startcol and linebreaks. */
722*946379e7Schristos goto recompute;
723*946379e7Schristos }
724*946379e7Schristos
725*946379e7Schristos /* Print the beginning of the line. This will depend on whether
726*946379e7Schristos this is the first line, and if the indented style is being
727*946379e7Schristos used. INDENT-F. */
728*946379e7Schristos if (line_prefix != NULL)
729*946379e7Schristos fputs (line_prefix, fp);
730*946379e7Schristos if (first_line)
731*946379e7Schristos {
732*946379e7Schristos fputs (name, fp);
733*946379e7Schristos if (indent)
734*946379e7Schristos {
735*946379e7Schristos if (extra_indent > 0)
736*946379e7Schristos fwrite (" ", 1, extra_indent, fp);
737*946379e7Schristos putc ('\t', fp);
738*946379e7Schristos }
739*946379e7Schristos else
740*946379e7Schristos putc (' ', fp);
741*946379e7Schristos first_line = false;
742*946379e7Schristos }
743*946379e7Schristos else
744*946379e7Schristos {
745*946379e7Schristos if (indent)
746*946379e7Schristos {
747*946379e7Schristos if (extra_indent > 0)
748*946379e7Schristos fwrite (" ", 1, extra_indent, fp);
749*946379e7Schristos putc ('\t', fp);
750*946379e7Schristos }
751*946379e7Schristos }
752*946379e7Schristos
753*946379e7Schristos /* Print the portion itself, with linebreaks where necessary. */
754*946379e7Schristos putc ('"', fp);
755*946379e7Schristos for (i = 0; i < portion_len; i++)
756*946379e7Schristos {
757*946379e7Schristos if (linebreaks[i] == UC_BREAK_POSSIBLE)
758*946379e7Schristos {
759*946379e7Schristos fputs ("\"\n", fp);
760*946379e7Schristos /* INDENT-S. */
761*946379e7Schristos if (line_prefix != NULL)
762*946379e7Schristos fputs (line_prefix, fp);
763*946379e7Schristos if (indent)
764*946379e7Schristos putc ('\t', fp);
765*946379e7Schristos putc ('"', fp);
766*946379e7Schristos }
767*946379e7Schristos putc (portion[i], fp);
768*946379e7Schristos }
769*946379e7Schristos fputs ("\"\n", fp);
770*946379e7Schristos
771*946379e7Schristos free (linebreaks);
772*946379e7Schristos free (overrides);
773*946379e7Schristos free (portion);
774*946379e7Schristos
775*946379e7Schristos s = es;
776*946379e7Schristos # undef is_escape
777*946379e7Schristos }
778*946379e7Schristos while (*s);
779*946379e7Schristos
780*946379e7Schristos #if HAVE_ICONV
781*946379e7Schristos if (conv != (iconv_t)(-1))
782*946379e7Schristos iconv_close (conv);
783*946379e7Schristos #endif
784*946379e7Schristos }
785*946379e7Schristos
786*946379e7Schristos
787*946379e7Schristos static void
print_blank_line(FILE * fp)788*946379e7Schristos print_blank_line (FILE *fp)
789*946379e7Schristos {
790*946379e7Schristos if (uniforum)
791*946379e7Schristos fputs ("#\n", fp);
792*946379e7Schristos else
793*946379e7Schristos putc ('\n', fp);
794*946379e7Schristos }
795*946379e7Schristos
796*946379e7Schristos
797*946379e7Schristos static void
message_print(const message_ty * mp,FILE * fp,const char * charset,size_t page_width,bool blank_line,bool debug)798*946379e7Schristos message_print (const message_ty *mp, FILE *fp, const char *charset,
799*946379e7Schristos size_t page_width, bool blank_line, bool debug)
800*946379e7Schristos {
801*946379e7Schristos int extra_indent;
802*946379e7Schristos
803*946379e7Schristos /* Separate messages with a blank line. Uniforum doesn't like blank
804*946379e7Schristos lines, so use an empty comment (unless there already is one). */
805*946379e7Schristos if (blank_line && (!uniforum
806*946379e7Schristos || mp->comment == NULL
807*946379e7Schristos || mp->comment->nitems == 0
808*946379e7Schristos || mp->comment->item[0][0] != '\0'))
809*946379e7Schristos print_blank_line (fp);
810*946379e7Schristos
811*946379e7Schristos /* Print translator comment if available. */
812*946379e7Schristos message_print_comment (mp, fp);
813*946379e7Schristos
814*946379e7Schristos /* Print xgettext extracted comments. */
815*946379e7Schristos message_print_comment_dot (mp, fp);
816*946379e7Schristos
817*946379e7Schristos /* Print the file position comments. This will help a human who is
818*946379e7Schristos trying to navigate the sources. There is no problem of getting
819*946379e7Schristos repeated positions, because duplicates are checked for. */
820*946379e7Schristos message_print_comment_filepos (mp, fp, uniforum, page_width);
821*946379e7Schristos
822*946379e7Schristos /* Print flag information in special comment. */
823*946379e7Schristos message_print_comment_flags (mp, fp, debug);
824*946379e7Schristos
825*946379e7Schristos /* Print the previous msgid. This helps the translator when the msgid has
826*946379e7Schristos only slightly changed. */
827*946379e7Schristos if (mp->prev_msgctxt != NULL)
828*946379e7Schristos wrap (mp, fp, "#| ", 0, "msgctxt", mp->prev_msgctxt, mp->do_wrap,
829*946379e7Schristos page_width, charset);
830*946379e7Schristos if (mp->prev_msgid != NULL)
831*946379e7Schristos wrap (mp, fp, "#| ", 0, "msgid", mp->prev_msgid, mp->do_wrap, page_width,
832*946379e7Schristos charset);
833*946379e7Schristos if (mp->prev_msgid_plural != NULL)
834*946379e7Schristos wrap (mp, fp, "#| ", 0, "msgid_plural", mp->prev_msgid_plural, mp->do_wrap,
835*946379e7Schristos page_width, charset);
836*946379e7Schristos extra_indent = (mp->prev_msgctxt != NULL || mp->prev_msgid != NULL
837*946379e7Schristos || mp->prev_msgid_plural != NULL
838*946379e7Schristos ? 3
839*946379e7Schristos : 0);
840*946379e7Schristos
841*946379e7Schristos /* Print each of the message components. Wrap them nicely so they
842*946379e7Schristos are as readable as possible. If there is no recorded msgstr for
843*946379e7Schristos this domain, emit an empty string. */
844*946379e7Schristos if (mp->msgctxt != NULL && !is_ascii_string (mp->msgctxt)
845*946379e7Schristos && po_charset_canonicalize (charset) != po_charset_utf8)
846*946379e7Schristos {
847*946379e7Schristos char *warning_message =
848*946379e7Schristos xasprintf (_("\
849*946379e7Schristos The following msgctxt contains non-ASCII characters.\n\
850*946379e7Schristos This will cause problems to translators who use a character encoding\n\
851*946379e7Schristos different from yours. Consider using a pure ASCII msgctxt instead.\n\
852*946379e7Schristos %s\n"), mp->msgctxt);
853*946379e7Schristos po_xerror (PO_SEVERITY_WARNING, mp, NULL, 0, 0, true, warning_message);
854*946379e7Schristos free (warning_message);
855*946379e7Schristos }
856*946379e7Schristos if (!is_ascii_string (mp->msgid)
857*946379e7Schristos && po_charset_canonicalize (charset) != po_charset_utf8)
858*946379e7Schristos {
859*946379e7Schristos char *warning_message =
860*946379e7Schristos xasprintf (_("\
861*946379e7Schristos The following msgid contains non-ASCII characters.\n\
862*946379e7Schristos This will cause problems to translators who use a character encoding\n\
863*946379e7Schristos different from yours. Consider using a pure ASCII msgid instead.\n\
864*946379e7Schristos %s\n"), mp->msgid);
865*946379e7Schristos po_xerror (PO_SEVERITY_WARNING, mp, NULL, 0, 0, true, warning_message);
866*946379e7Schristos free (warning_message);
867*946379e7Schristos }
868*946379e7Schristos if (mp->msgctxt != NULL)
869*946379e7Schristos wrap (mp, fp, NULL, extra_indent, "msgctxt", mp->msgctxt, mp->do_wrap,
870*946379e7Schristos page_width, charset);
871*946379e7Schristos wrap (mp, fp, NULL, extra_indent, "msgid", mp->msgid, mp->do_wrap,
872*946379e7Schristos page_width, charset);
873*946379e7Schristos if (mp->msgid_plural != NULL)
874*946379e7Schristos wrap (mp, fp, NULL, extra_indent, "msgid_plural", mp->msgid_plural,
875*946379e7Schristos mp->do_wrap, page_width, charset);
876*946379e7Schristos
877*946379e7Schristos if (mp->msgid_plural == NULL)
878*946379e7Schristos wrap (mp, fp, NULL, extra_indent, "msgstr", mp->msgstr, mp->do_wrap,
879*946379e7Schristos page_width, charset);
880*946379e7Schristos else
881*946379e7Schristos {
882*946379e7Schristos char prefix_buf[20];
883*946379e7Schristos unsigned int i;
884*946379e7Schristos const char *p;
885*946379e7Schristos
886*946379e7Schristos for (p = mp->msgstr, i = 0;
887*946379e7Schristos p < mp->msgstr + mp->msgstr_len;
888*946379e7Schristos p += strlen (p) + 1, i++)
889*946379e7Schristos {
890*946379e7Schristos sprintf (prefix_buf, "msgstr[%u]", i);
891*946379e7Schristos wrap (mp, fp, NULL, extra_indent, prefix_buf, p, mp->do_wrap,
892*946379e7Schristos page_width, charset);
893*946379e7Schristos }
894*946379e7Schristos }
895*946379e7Schristos }
896*946379e7Schristos
897*946379e7Schristos
898*946379e7Schristos static void
message_print_obsolete(const message_ty * mp,FILE * fp,const char * charset,size_t page_width,bool blank_line)899*946379e7Schristos message_print_obsolete (const message_ty *mp, FILE *fp, const char *charset,
900*946379e7Schristos size_t page_width, bool blank_line)
901*946379e7Schristos {
902*946379e7Schristos int extra_indent;
903*946379e7Schristos
904*946379e7Schristos /* If msgstr is the empty string we print nothing. */
905*946379e7Schristos if (mp->msgstr[0] == '\0')
906*946379e7Schristos return;
907*946379e7Schristos
908*946379e7Schristos /* Separate messages with a blank line. Uniforum doesn't like blank
909*946379e7Schristos lines, so use an empty comment (unless there already is one). */
910*946379e7Schristos if (blank_line)
911*946379e7Schristos print_blank_line (fp);
912*946379e7Schristos
913*946379e7Schristos /* Print translator comment if available. */
914*946379e7Schristos message_print_comment (mp, fp);
915*946379e7Schristos
916*946379e7Schristos /* Print xgettext extracted comments (normally empty). */
917*946379e7Schristos message_print_comment_dot (mp, fp);
918*946379e7Schristos
919*946379e7Schristos /* Print the file position comments (normally empty). */
920*946379e7Schristos message_print_comment_filepos (mp, fp, uniforum, page_width);
921*946379e7Schristos
922*946379e7Schristos /* Print flag information in special comment. */
923*946379e7Schristos if (mp->is_fuzzy)
924*946379e7Schristos {
925*946379e7Schristos bool first = true;
926*946379e7Schristos
927*946379e7Schristos putc ('#', fp);
928*946379e7Schristos putc (',', fp);
929*946379e7Schristos
930*946379e7Schristos if (mp->is_fuzzy)
931*946379e7Schristos {
932*946379e7Schristos fputs (" fuzzy", fp);
933*946379e7Schristos first = false;
934*946379e7Schristos }
935*946379e7Schristos
936*946379e7Schristos putc ('\n', fp);
937*946379e7Schristos }
938*946379e7Schristos
939*946379e7Schristos /* Print the previous msgid. This helps the translator when the msgid has
940*946379e7Schristos only slightly changed. */
941*946379e7Schristos if (mp->prev_msgctxt != NULL)
942*946379e7Schristos wrap (mp, fp, "#~| ", 0, "msgctxt", mp->prev_msgctxt, mp->do_wrap,
943*946379e7Schristos page_width, charset);
944*946379e7Schristos if (mp->prev_msgid != NULL)
945*946379e7Schristos wrap (mp, fp, "#~| ", 0, "msgid", mp->prev_msgid, mp->do_wrap, page_width,
946*946379e7Schristos charset);
947*946379e7Schristos if (mp->prev_msgid_plural != NULL)
948*946379e7Schristos wrap (mp, fp, "#~| ", 0, "msgid_plural", mp->prev_msgid_plural, mp->do_wrap,
949*946379e7Schristos page_width, charset);
950*946379e7Schristos extra_indent = (mp->prev_msgctxt != NULL || mp->prev_msgid != NULL
951*946379e7Schristos || mp->prev_msgid_plural != NULL
952*946379e7Schristos ? 1
953*946379e7Schristos : 0);
954*946379e7Schristos
955*946379e7Schristos /* Print each of the message components. Wrap them nicely so they
956*946379e7Schristos are as readable as possible. */
957*946379e7Schristos if (mp->msgctxt != NULL && !is_ascii_string (mp->msgctxt)
958*946379e7Schristos && po_charset_canonicalize (charset) != po_charset_utf8)
959*946379e7Schristos {
960*946379e7Schristos char *warning_message =
961*946379e7Schristos xasprintf (_("\
962*946379e7Schristos The following msgctxt contains non-ASCII characters.\n\
963*946379e7Schristos This will cause problems to translators who use a character encoding\n\
964*946379e7Schristos different from yours. Consider using a pure ASCII msgctxt instead.\n\
965*946379e7Schristos %s\n"), mp->msgctxt);
966*946379e7Schristos po_xerror (PO_SEVERITY_WARNING, mp, NULL, 0, 0, true, warning_message);
967*946379e7Schristos free (warning_message);
968*946379e7Schristos }
969*946379e7Schristos if (!is_ascii_string (mp->msgid)
970*946379e7Schristos && po_charset_canonicalize (charset) != po_charset_utf8)
971*946379e7Schristos {
972*946379e7Schristos char *warning_message =
973*946379e7Schristos xasprintf (_("\
974*946379e7Schristos The following msgid contains non-ASCII characters.\n\
975*946379e7Schristos This will cause problems to translators who use a character encoding\n\
976*946379e7Schristos different from yours. Consider using a pure ASCII msgid instead.\n\
977*946379e7Schristos %s\n"), mp->msgid);
978*946379e7Schristos po_xerror (PO_SEVERITY_WARNING, mp, NULL, 0, 0, true, warning_message);
979*946379e7Schristos free (warning_message);
980*946379e7Schristos }
981*946379e7Schristos if (mp->msgctxt != NULL)
982*946379e7Schristos wrap (mp, fp, "#~ ", extra_indent, "msgctxt", mp->msgctxt, mp->do_wrap,
983*946379e7Schristos page_width, charset);
984*946379e7Schristos wrap (mp, fp, "#~ ", extra_indent, "msgid", mp->msgid, mp->do_wrap,
985*946379e7Schristos page_width, charset);
986*946379e7Schristos if (mp->msgid_plural != NULL)
987*946379e7Schristos wrap (mp, fp, "#~ ", extra_indent, "msgid_plural", mp->msgid_plural,
988*946379e7Schristos mp->do_wrap, page_width, charset);
989*946379e7Schristos
990*946379e7Schristos if (mp->msgid_plural == NULL)
991*946379e7Schristos wrap (mp, fp, "#~ ", extra_indent, "msgstr", mp->msgstr, mp->do_wrap,
992*946379e7Schristos page_width, charset);
993*946379e7Schristos else
994*946379e7Schristos {
995*946379e7Schristos char prefix_buf[20];
996*946379e7Schristos unsigned int i;
997*946379e7Schristos const char *p;
998*946379e7Schristos
999*946379e7Schristos for (p = mp->msgstr, i = 0;
1000*946379e7Schristos p < mp->msgstr + mp->msgstr_len;
1001*946379e7Schristos p += strlen (p) + 1, i++)
1002*946379e7Schristos {
1003*946379e7Schristos sprintf (prefix_buf, "msgstr[%u]", i);
1004*946379e7Schristos wrap (mp, fp, "#~ ", extra_indent, prefix_buf, p, mp->do_wrap,
1005*946379e7Schristos page_width, charset);
1006*946379e7Schristos }
1007*946379e7Schristos }
1008*946379e7Schristos }
1009*946379e7Schristos
1010*946379e7Schristos
1011*946379e7Schristos static void
msgdomain_list_print_po(msgdomain_list_ty * mdlp,FILE * fp,size_t page_width,bool debug)1012*946379e7Schristos msgdomain_list_print_po (msgdomain_list_ty *mdlp, FILE *fp, size_t page_width,
1013*946379e7Schristos bool debug)
1014*946379e7Schristos {
1015*946379e7Schristos size_t j, k;
1016*946379e7Schristos bool blank_line;
1017*946379e7Schristos
1018*946379e7Schristos /* Write out the messages for each domain. */
1019*946379e7Schristos blank_line = false;
1020*946379e7Schristos for (k = 0; k < mdlp->nitems; k++)
1021*946379e7Schristos {
1022*946379e7Schristos message_list_ty *mlp;
1023*946379e7Schristos const char *header;
1024*946379e7Schristos const char *charset;
1025*946379e7Schristos char *allocated_charset;
1026*946379e7Schristos
1027*946379e7Schristos /* If the first domain is the default, don't bother emitting
1028*946379e7Schristos the domain name, because it is the default. */
1029*946379e7Schristos if (!(k == 0
1030*946379e7Schristos && strcmp (mdlp->item[k]->domain, MESSAGE_DOMAIN_DEFAULT) == 0))
1031*946379e7Schristos {
1032*946379e7Schristos if (blank_line)
1033*946379e7Schristos print_blank_line (fp);
1034*946379e7Schristos fprintf (fp, "domain \"%s\"\n", mdlp->item[k]->domain);
1035*946379e7Schristos blank_line = true;
1036*946379e7Schristos }
1037*946379e7Schristos
1038*946379e7Schristos mlp = mdlp->item[k]->messages;
1039*946379e7Schristos
1040*946379e7Schristos /* Search the header entry. */
1041*946379e7Schristos header = NULL;
1042*946379e7Schristos for (j = 0; j < mlp->nitems; ++j)
1043*946379e7Schristos if (is_header (mlp->item[j]) && !mlp->item[j]->obsolete)
1044*946379e7Schristos {
1045*946379e7Schristos header = mlp->item[j]->msgstr;
1046*946379e7Schristos break;
1047*946379e7Schristos }
1048*946379e7Schristos
1049*946379e7Schristos /* Extract the charset name. */
1050*946379e7Schristos charset = "ASCII";
1051*946379e7Schristos allocated_charset = NULL;
1052*946379e7Schristos if (header != NULL)
1053*946379e7Schristos {
1054*946379e7Schristos const char *charsetstr = c_strstr (header, "charset=");
1055*946379e7Schristos
1056*946379e7Schristos if (charsetstr != NULL)
1057*946379e7Schristos {
1058*946379e7Schristos size_t len;
1059*946379e7Schristos
1060*946379e7Schristos charsetstr += strlen ("charset=");
1061*946379e7Schristos len = strcspn (charsetstr, " \t\n");
1062*946379e7Schristos allocated_charset = (char *) xallocsa (len + 1);
1063*946379e7Schristos memcpy (allocated_charset, charsetstr, len);
1064*946379e7Schristos allocated_charset[len] = '\0';
1065*946379e7Schristos charset = allocated_charset;
1066*946379e7Schristos
1067*946379e7Schristos /* Treat the dummy default value as if it were absent. */
1068*946379e7Schristos if (strcmp (charset, "CHARSET") == 0)
1069*946379e7Schristos charset = "ASCII";
1070*946379e7Schristos }
1071*946379e7Schristos }
1072*946379e7Schristos
1073*946379e7Schristos /* Write out each of the messages for this domain. */
1074*946379e7Schristos for (j = 0; j < mlp->nitems; ++j)
1075*946379e7Schristos if (!mlp->item[j]->obsolete)
1076*946379e7Schristos {
1077*946379e7Schristos message_print (mlp->item[j], fp, charset, page_width, blank_line,
1078*946379e7Schristos debug);
1079*946379e7Schristos blank_line = true;
1080*946379e7Schristos }
1081*946379e7Schristos
1082*946379e7Schristos /* Write out each of the obsolete messages for this domain. */
1083*946379e7Schristos for (j = 0; j < mlp->nitems; ++j)
1084*946379e7Schristos if (mlp->item[j]->obsolete)
1085*946379e7Schristos {
1086*946379e7Schristos message_print_obsolete (mlp->item[j], fp, charset, page_width,
1087*946379e7Schristos blank_line);
1088*946379e7Schristos blank_line = true;
1089*946379e7Schristos }
1090*946379e7Schristos
1091*946379e7Schristos if (allocated_charset != NULL)
1092*946379e7Schristos freesa (allocated_charset);
1093*946379e7Schristos }
1094*946379e7Schristos }
1095*946379e7Schristos
1096*946379e7Schristos
1097*946379e7Schristos /* Describes a PO file in .po syntax. */
1098*946379e7Schristos const struct catalog_output_format output_format_po =
1099*946379e7Schristos {
1100*946379e7Schristos msgdomain_list_print_po, /* print */
1101*946379e7Schristos false, /* requires_utf8 */
1102*946379e7Schristos true, /* supports_multiple_domains */
1103*946379e7Schristos true, /* supports_contexts */
1104*946379e7Schristos true, /* supports_plurals */
1105*946379e7Schristos false, /* alternative_is_po */
1106*946379e7Schristos false /* alternative_is_java_class */
1107*946379e7Schristos };
1108