]>
Commit | Line | Data |
---|---|---|
fb4f7b36 | 1 | diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/Makefile.am gettext.hacked/Makefile.am |
2 | --- gettext.initial/Makefile.am Mon Feb 22 13:40:47 1999 | |
3 | +++ gettext.hacked/Makefile.am Mon Feb 22 14:10:19 1999 | |
4 | @@ -19,7 +19,7 @@ | |
5 | ||
6 | AUTOMAKE_OPTIONS = 1.3 gnits | |
7 | MAINT_CHARSET = latin1 | |
8 | -ACLOCAL_AMFLAGS = -I m4 | |
9 | +ACLOCAL_AMFLAGS = --acdir=m4 --acdir=$(shell aclocal --print-ac-dir) | |
10 | ||
11 | gettextsrcdir = $(datadir)/gettext | |
12 | gettextsrc_DATA = ABOUT-NLS | |
13 | diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/configure.in gettext.hacked/configure.in | |
14 | --- gettext.initial/configure.in Mon Feb 22 13:40:47 1999 | |
15 | +++ gettext.hacked/configure.in Mon Feb 22 14:10:20 1999 | |
16 | @@ -12,7 +12,7 @@ | |
17 | AM_PROG_LIBTOOL | |
18 | AC_PROG_CC | |
19 | AC_ISC_POSIX | |
20 | -AM_PROG_INSTALL | |
21 | +AC_PROG_INSTALL | |
22 | AC_PROG_YACC | |
23 | ||
24 | dnl Checks for libraries. | |
25 | diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/doc/stamp-vti gettext.hacked/doc/stamp-vti | |
26 | --- gettext.initial/doc/stamp-vti Mon Feb 22 13:40:47 1999 | |
27 | +++ gettext.hacked/doc/stamp-vti Mon Apr 19 18:54:47 1999 | |
28 | @@ -1,3 +1,3 @@ | |
29 | -@set UPDATED 30 April 1998 | |
30 | +@set UPDATED 21 March 1999 | |
31 | @set EDITION 0.10.35 | |
32 | @set VERSION 0.10.35 | |
33 | diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/doc/version.texi gettext.hacked/doc/version.texi | |
34 | --- gettext.initial/doc/version.texi Mon Feb 22 13:40:47 1999 | |
35 | +++ gettext.hacked/doc/version.texi Mon Apr 19 18:54:47 1999 | |
36 | @@ -1,3 +1,3 @@ | |
37 | -@set UPDATED 30 April 1998 | |
38 | +@set UPDATED 21 March 1999 | |
39 | @set EDITION 0.10.35 | |
40 | @set VERSION 0.10.35 | |
c155f01f | 41 | diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/po/gettext.pot gettext.hacked/po/gettext.pot |
42 | --- gettext.initial/po/gettext.pot Mon Feb 22 13:40:48 1999 | |
43 | +++ gettext.hacked/po/gettext.pot Thu May 6 17:32:13 1999 | |
44 | @@ -6,7 +6,7 @@ | |
45 | msgid "" | |
46 | msgstr "" | |
47 | "Project-Id-Version: PACKAGE VERSION\n" | |
48 | -"POT-Creation-Date: 1998-04-30 22:50-0700\n" | |
49 | +"POT-Creation-Date: 1999-05-05 18:24-0400\n" | |
50 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | |
51 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | |
52 | "Language-Team: LANGUAGE <LL@li.org>\n" | |
53 | @@ -143,23 +143,23 @@ | |
54 | msgid "Report bugs to <bug-gnu-utils@gnu.org>.\n" | |
55 | msgstr "" | |
56 | ||
57 | -#: src/message.c:784 | |
58 | +#: src/message.c:787 | |
59 | #, c-format | |
60 | msgid "" | |
61 | "internationalized messages should not contain the `\\%c' escape sequence" | |
62 | msgstr "" | |
63 | ||
64 | -#: src/message.c:1115 | |
65 | +#: src/message.c:1268 src/message.c:1586 | |
66 | #, c-format | |
67 | msgid "cannot create output file \"%s\"" | |
68 | msgstr "" | |
69 | ||
70 | -#: src/message.c:1122 | |
71 | +#: src/message.c:1275 src/message.c:1591 | |
72 | #, no-c-format | |
73 | msgid "standard output" | |
74 | msgstr "" | |
75 | ||
76 | -#: src/message.c:1182 | |
77 | +#: src/message.c:1335 src/message.c:1633 | |
78 | #, c-format | |
79 | msgid "error while writing \"%s\" file" | |
80 | msgstr "" | |
fb4f7b36 | 81 | diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/src/Makefile.am gettext.hacked/src/Makefile.am |
82 | --- gettext.initial/src/Makefile.am Mon Feb 22 13:40:48 1999 | |
83 | +++ gettext.hacked/src/Makefile.am Mon Feb 22 18:43:39 1999 | |
84 | @@ -19,7 +19,7 @@ | |
85 | ||
86 | AUTOMAKE_OPTIONS = 1.2 gnits | |
87 | ||
88 | -bin_PROGRAMS = gettext msgcmp msgfmt msgmerge msgunfmt xgettext msgcomm | |
89 | +bin_PROGRAMS = gettext msgcmp msgfmt msgmerge msgunfmt xgettext msgcomm msghack | |
90 | ||
91 | noinst_HEADERS = domain.h message.h po-gram.h po-hash.h po-lex.h po.h \ | |
92 | str-list.h xget-lex.h po-gram.gen.h po-hash.gen.h dir-list.h | |
93 | @@ -52,7 +52,8 @@ | |
94 | po.c str-list.c xget-lex.c xgettext.c dir-list.c | |
95 | msgcomm_SOURCES = msgcomm.c message.c po-gram.gen.c po-hash.gen.c po-lex.c \ | |
96 | open-po.c po.c str-list.c dir-list.c | |
97 | - | |
98 | +msghack_SOURCES = msghack.c open-po.c dir-list.c message.c str-list.c \ | |
99 | +po-gram.gen.c po-hash.gen.c po-lex.c po.c | |
100 | MAINTAINERCLEANFILES = po-gram.gen.c po-gram.gen.h po-hash.gen.c po-hash.gen.h | |
101 | ||
102 | # Some rules for yacc handling. | |
103 | diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/src/message.c gettext.hacked/src/message.c | |
104 | --- gettext.initial/src/message.c Mon Feb 22 13:40:48 1999 | |
105 | +++ gettext.hacked/src/message.c Mon Apr 19 05:21:23 1999 | |
106 | @@ -68,6 +68,9 @@ | |
107 | static void message_print PARAMS ((const message_ty *__mp, FILE *__fp, | |
108 | const char *__domain, int blank_line, | |
109 | int __debug)); | |
110 | +static void master_message_print PARAMS ((const message_ty *__mp, | |
111 | + FILE *__fp, | |
112 | + int blank_line)); | |
113 | static void message_print_obsolete PARAMS ((const message_ty *__mp, FILE *__fp, | |
114 | const char *__domain, | |
115 | int blank_line)); | |
116 | @@ -990,6 +993,156 @@ | |
117 | ||
118 | ||
119 | static void | |
120 | +master_message_print (mp, fp, blank_line) | |
121 | + const message_ty *mp; | |
122 | + FILE *fp; | |
123 | + int blank_line; | |
124 | +{ | |
125 | + size_t j; | |
126 | + | |
127 | + /* Separate messages with a blank line. Uniforum doesn't like blank | |
128 | + lines, so use an empty comment (unless there already is one). */ | |
129 | + if (blank_line && (!uniforum | |
130 | + || mp->comment == NULL | |
131 | + || mp->comment->nitems == 0 | |
132 | + || mp->comment->item[0][0] != '\0')) | |
133 | + print_blank_line (fp); | |
134 | + | |
135 | + if (mp->comment != NULL) | |
136 | + for (j = 0; j < mp->comment->nitems; ++j) { | |
137 | + const unsigned char *s = mp->comment->item[j]; | |
138 | + do { | |
139 | + const unsigned char *e; | |
140 | + putc ('#', fp); | |
141 | + /* FIXME This is the wrong locale. While | |
142 | + message_list_print set the "C" locale for LC_CTYPE, | |
143 | + the need to use the correct locale for the file's | |
144 | + contents. */ | |
145 | + if (*s != '\0' && !isspace (*s)) | |
146 | + putc (' ', fp); | |
147 | + e = strchr (s, '\n'); | |
148 | + if (e == NULL) { | |
149 | + fputs (s, fp); | |
150 | + s = NULL; | |
151 | + } else { | |
152 | + fwrite (s, 1, e - s, fp); | |
153 | + s = e + 1; | |
154 | + } | |
155 | + putc ('\n', fp); | |
156 | + } while (s != NULL); | |
157 | + } | |
158 | + | |
159 | + if (mp->comment_dot != NULL) | |
160 | + for (j = 0; j < mp->comment_dot->nitems; ++j) { | |
161 | + const unsigned char *s = mp->comment_dot->item[j]; | |
162 | + putc ('#', fp); | |
163 | + putc ('.', fp); | |
164 | + /* FIXME This is the wrong locale. While | |
165 | + message_list_print set the "C" locale for LC_CTYPE, the | |
166 | + need to use the correct locale for the file's contents. */ | |
167 | + if (*s && !isspace (*s)) | |
168 | + putc (' ', fp); | |
169 | + fputs (s, fp); | |
170 | + putc ('\n', fp); | |
171 | + } | |
172 | + | |
173 | + | |
174 | + /* Print the file position comments for every domain. This will | |
175 | + help a human who is trying to navigate the sources. There is no | |
176 | + problem of getting repeat positions, because duplicates are | |
177 | + checked for. */ | |
178 | + if (mp->filepos_count != 0) { | |
179 | + if (uniforum) | |
180 | + for (j = 0; j < mp->filepos_count; ++j) { | |
181 | + lex_pos_ty *pp = &mp->filepos[j]; | |
182 | + char *cp = pp->file_name; | |
183 | + while (cp[0] == '.' && cp[1] == '/') | |
184 | + cp += 2; | |
185 | + /* There are two Sun formats to choose from: SunOS and | |
186 | + Solaris. Use the Solaris form here. */ | |
187 | + fprintf (fp, "# File: %s, line: %ld\n", | |
188 | + cp, (long) pp->line_number); | |
189 | + } else { | |
190 | + size_t column; | |
191 | + | |
192 | + fputs ("#:", fp); | |
193 | + column = 2; | |
194 | + for (j = 0; j < mp->filepos_count; ++j) { | |
195 | + lex_pos_ty *pp; | |
196 | + char buffer[20]; | |
197 | + char *cp; | |
198 | + size_t len; | |
199 | + | |
200 | + pp = &mp->filepos[j]; | |
201 | + cp = pp->file_name; | |
202 | + while (cp[0] == '.' && cp[1] == '/') | |
203 | + cp += 2; | |
204 | + sprintf (buffer, "%ld", (long) pp->line_number); | |
205 | + len = strlen (cp) + strlen (buffer) + 2; | |
206 | + if (column > 2 && column + len >= page_width) | |
207 | + { | |
208 | + fputs ("\n#:", fp); | |
209 | + column = 2; | |
210 | + } | |
211 | + fprintf (fp, " %s:%s", cp, buffer); | |
212 | + column += len; | |
213 | + } | |
214 | + putc ('\n', fp); | |
215 | + } | |
216 | + } | |
217 | + | |
218 | + /* Print flag information in special comment. */ | |
219 | + if (mp->is_fuzzy || significant_c_format_p (mp->is_c_format) || mp->do_wrap == no) { | |
220 | + int first_flag = 1; | |
221 | + | |
222 | + putc ('#', fp); | |
223 | + putc (',', fp); | |
224 | + | |
225 | + /* We don't print the fuzzy flag if the msgstr is empty. This | |
226 | + might be introduced by the user but we want to normalize the | |
227 | + output. */ | |
228 | + if (mp->is_fuzzy) { | |
229 | + fputs (" fuzzy", fp); | |
230 | + first_flag = 0; | |
231 | + } | |
232 | + | |
233 | + if (significant_c_format_p (mp->is_c_format)) { | |
234 | + if (!first_flag) | |
235 | + putc (',', fp); | |
236 | + | |
237 | + fputs (make_c_format_description_string (mp->is_c_format, 0), | |
238 | + fp); | |
239 | + first_flag = 0; | |
240 | + } | |
241 | + | |
242 | + if (mp->do_wrap == no) { | |
243 | + if (!first_flag) | |
244 | + putc (',', fp); | |
245 | + | |
246 | + fputs (make_c_width_description_string (mp->do_wrap), fp); | |
247 | + first_flag = 0; | |
248 | + } | |
249 | + | |
250 | + putc ('\n', fp); | |
251 | + } | |
252 | + | |
253 | + /* Print each of the message components. Wrap them nicely so they | |
254 | + are as readable as possible. If there is no recorded msgstr for | |
255 | + this domain, emit an empty string. */ | |
256 | + wrap (fp, NULL, "msgid", mp->msgid, mp->do_wrap); | |
257 | + for (j = 0; j < mp->variant_count; ++j) { | |
258 | + char *msgstr; | |
259 | + | |
260 | + msgstr = malloc(strlen(mp->variant[j].domain) + 10); | |
261 | + sprintf(msgstr, "msgstr(%s)", mp->variant[j].domain); | |
262 | + | |
263 | + wrap (fp, NULL, msgstr, mp->variant[j].msgstr, mp->do_wrap); | |
264 | + } | |
265 | + | |
266 | +} | |
267 | + | |
268 | + | |
269 | +static void | |
270 | message_print_obsolete (mp, fp, domain, blank_line) | |
271 | const message_ty *mp; | |
272 | FILE *fp; | |
273 | @@ -1402,3 +1555,83 @@ | |
274 | ||
275 | page_width = n; | |
276 | } | |
277 | + | |
278 | + | |
279 | +/* | |
280 | + * HACKS... This prints a mesage list, but it uses the | |
281 | + * msgstr(somain) syntax instead | |
282 | + */ | |
283 | + void | |
284 | +master_list_print (mlp, filename) | |
285 | + message_list_ty *mlp; | |
286 | + const char *filename; | |
287 | +{ | |
288 | + FILE *fp; | |
289 | + size_t j; | |
290 | + int blank_line; | |
291 | +#ifdef HAVE_SETLOCALE | |
292 | + char *old_locale; | |
293 | +#endif | |
294 | + | |
295 | + /* We will not write anything if we have no message or only the | |
296 | + header entry. */ | |
297 | + if (mlp->nitems == 0 || (mlp->nitems == 1 && *mlp->item[0]->msgid == '\0')) | |
298 | + return; | |
299 | + | |
300 | + /* Open the output file. */ | |
301 | + if (filename != NULL && strcmp (filename, "-") != 0 | |
302 | + && strcmp (filename, "/dev/stdout") != 0) { | |
303 | + fp = fopen (filename, "w"); | |
304 | + if (fp == NULL) | |
305 | + error (EXIT_FAILURE, errno, _("cannot create output file \"%s\""), | |
306 | + filename); | |
307 | + } else { | |
308 | + fp = stdout; | |
309 | + /* xgettext:no-c-format */ | |
310 | + filename = _("standard output"); | |
311 | + } | |
312 | + | |
313 | +#ifdef HAVE_SETLOCALE | |
314 | + /* FIXME This is the wrong locale. The program is currently set for | |
315 | + the user's native language locale, for the error messages. This | |
316 | + code sets it to the "C" locale, but that isn't right either. The | |
317 | + need is to use the correct locale for the file's contents. */ | |
318 | + old_locale = setlocale (LC_CTYPE, "C"); | |
319 | + if (old_locale) | |
320 | + old_locale = xstrdup (old_locale); | |
321 | +#endif | |
322 | + | |
323 | + /* Write out each of the messages for this domain. */ | |
324 | + for (j = 0; j < mlp->nitems; ++j) | |
325 | + if (mlp->item[j]->obsolete == 0) { | |
326 | + master_message_print (mlp->item[j], fp, blank_line); | |
327 | + blank_line = 1; | |
328 | + } | |
329 | + | |
330 | +#if 0 /* screw the obsoletes */ | |
331 | + /* Write out each of the obsolete messages for this domain. */ | |
332 | + for (j = 0; j < mlp->nitems; ++j) | |
333 | + if (mlp->item[j]->obsolete != 0) { | |
334 | + message_print_obsolete (mlp->item[j], fp, dl->item[k], blank_line); | |
335 | + blank_line = 1; | |
336 | + } | |
337 | +#endif | |
338 | + | |
339 | + /* Restore the old locale. Do this before emitting error messages, | |
340 | + so that the correct locale is used for the error. (Ideally, | |
341 | + error should ensure this before calling gettext for the format | |
342 | + string.) */ | |
343 | +#ifdef HAVE_SETLOCALE | |
344 | + if (old_locale) { | |
345 | + setlocale (LC_CTYPE, old_locale); | |
346 | + free (old_locale); | |
347 | + } | |
348 | +#endif | |
349 | + | |
350 | + /* Make sure nothing went wrong. */ | |
351 | + if (fflush (fp)) | |
352 | + error (EXIT_FAILURE, errno, _("error while writing \"%s\" file"), | |
353 | + filename); | |
354 | + fclose (fp); | |
355 | +} | |
356 | + | |
357 | diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/src/message.h gettext.hacked/src/message.h | |
358 | --- gettext.initial/src/message.h Mon Feb 22 13:40:48 1999 | |
359 | +++ gettext.hacked/src/message.h Mon Apr 19 05:21:23 1999 | |
360 | @@ -126,4 +126,7 @@ | |
361 | int possible_c_format_p PARAMS ((enum is_c_format)); | |
362 | void message_page_width_set PARAMS ((size_t width)); | |
363 | ||
364 | +/* Hacks go at the end */ | |
365 | +void master_list_print PARAMS ((message_list_ty *__mlp, const char *__filename)); | |
366 | + | |
367 | #endif /* message.h */ | |
368 | diff -urN --exclude Makefile.in --exclude ltconfig --exclude ltmain.sh --exclude configure --exclude build --exclude CVS --exclude config.* --exclude genpatch --exclude .cvsignore --exclude aclocal.m4 --exclude *.po --exclude *.gmo --exclude gettext-0.10.35-hacks.patch gettext.initial/src/msghack.c gettext.hacked/src/msghack.c | |
369 | --- gettext.initial/src/msghack.c Wed Dec 31 19:00:00 1969 | |
c155f01f | 370 | +++ gettext.hacked/src/msghack.c Sun Feb 27 13:30:00 2000 |
371 | @@ -0,0 +1,1606 @@ | |
fb4f7b36 | 372 | +/* GNU gettext - internationalization aids |
373 | + Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. | |
374 | + This file was written by Cristian Gafton <gafton@redhat.com> | |
375 | + | |
376 | + This program is free software; you can redistribute it and/or modify | |
377 | + it under the terms of the GNU General Public License as published by | |
378 | + the Free Software Foundation; either version 2, or (at your option) | |
379 | + any later version. | |
380 | + | |
381 | + This program is distributed in the hope that it will be useful, | |
382 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
383 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
384 | + GNU General Public License for more details. | |
385 | + | |
386 | + You should have received a copy of the GNU General Public License | |
387 | + along with this program; if not, write to the Free Software Foundation, | |
388 | + Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ | |
389 | + | |
390 | +#ifdef HAVE_CONFIG_H | |
391 | +# include <config.h> | |
392 | +#endif | |
393 | + | |
394 | +#include <getopt.h> | |
395 | +#include <limits.h> | |
396 | +#include <stdio.h> | |
397 | + | |
398 | +#ifdef STDC_HEADERS | |
399 | +# include <stdlib.h> | |
400 | +#endif | |
401 | + | |
402 | +#if HAVE_STRING_H | |
403 | +# include <string.h> | |
404 | +#else | |
405 | +# include <strings.h> | |
406 | +#endif | |
407 | + | |
408 | +#if HAVE_LOCALE_H | |
409 | +# include <locale.h> | |
410 | +#endif | |
411 | + | |
412 | +#include <time.h> | |
413 | +#include <errno.h> | |
414 | + | |
415 | +#include "dir-list.h" | |
416 | +#include "error.h" | |
417 | +#include "message.h" | |
418 | +#include <system.h> | |
419 | +#include <libintl.h> | |
420 | +#include "po.h" | |
421 | + | |
422 | +#define _(str) gettext (str) | |
423 | + | |
424 | + | |
425 | +/* This structure defines a derived class of the po_ty class. (See | |
426 | + po.h for an explanation.) */ | |
427 | +typedef struct merge_class_ty merge_class_ty; | |
428 | +struct merge_class_ty | |
429 | +{ | |
430 | + /* inherited instance variables, etc */ | |
431 | + PO_BASE_TY | |
432 | + | |
433 | + /* Name of domain we are currently examining. */ | |
434 | + char *domain; | |
435 | + | |
436 | + /* List of domains already appeared in the current file. */ | |
437 | + string_list_ty *domain_list; | |
438 | + | |
439 | + /* List of messages already appeared in the current file. */ | |
440 | + message_list_ty *mlp; | |
441 | + | |
442 | + /* Accumulate comments for next message directive */ | |
443 | + string_list_ty *comment; | |
444 | + string_list_ty *comment_dot; | |
445 | + | |
446 | + /* Flags transported in special comments. */ | |
447 | + int is_fuzzy; | |
448 | + enum is_c_format is_c_format; | |
449 | + enum is_c_format do_wrap; | |
450 | + | |
451 | + /* Accumulate filepos comments for the next message directive. */ | |
452 | + size_t filepos_count; | |
453 | + lex_pos_ty *filepos; | |
454 | +}; | |
455 | + | |
456 | + | |
457 | +/* String containing name the program is called with. */ | |
458 | +const char *program_name; | |
459 | + | |
460 | +/* Verbosity level. */ | |
461 | +static int verbosity_level; | |
462 | + | |
463 | +/* If nonzero, remember comments for file name and line number for each | |
464 | + msgid, if present in the reference input. Defaults to true. */ | |
465 | +static int line_comment = 1; | |
466 | + | |
467 | +/* Force output of PO file even if empty. */ | |
468 | +static int force_po; | |
469 | + | |
470 | +/* should we merge in the fuzzy messages ? */ | |
471 | +static int fuzzy = 0; | |
472 | + | |
c155f01f | 473 | +/* Do we want to generate useless merges? */ |
474 | +static int useless = 0; | |
475 | + | |
fb4f7b36 | 476 | +/* Possible split operations */ |
477 | +#define SPLIT_MSGID 1 | |
478 | +#define SPLIT_MSGSTR 2 | |
479 | +static int split_flag = 0; | |
480 | + | |
481 | +/* Possible explode operations */ | |
482 | +#define EXPLODE_MSGID 4 | |
483 | +#define EXPLODE_MSGSTR 8 | |
484 | +static int explode_flag = 0; | |
485 | + | |
486 | +/* Sort orders */ | |
487 | +#define SORT_BY_FILEPOS 1 | |
488 | +#define SORT_BY_MSGID 2 | |
489 | +static int sort_order = 0; | |
490 | + | |
c155f01f | 491 | +/* Other flags */ |
492 | +static int comments = 0; | |
493 | + | |
fb4f7b36 | 494 | +/* Prototypes for local functions. */ |
495 | +static void usage PARAMS ((int __status)); | |
496 | +static void error_print PARAMS ((void)); | |
497 | +static void merge_constructor PARAMS ((po_ty *__that)); | |
498 | +static void merge_destructor PARAMS ((po_ty *__that)); | |
499 | +static void merge_directive_domain PARAMS ((po_ty *__that, char *__name)); | |
500 | +static void merge_directive_message PARAMS ((po_ty *__that, char *__msgid, | |
501 | + lex_pos_ty *__msgid_pos, | |
502 | + char *__msgstr, lex_pos_ty *__msgstr_pos)); | |
503 | +static void merge_parse_brief PARAMS ((po_ty *__that)); | |
504 | +static void merge_parse_debrief PARAMS ((po_ty *__that)); | |
505 | +static void merge_comment PARAMS ((po_ty *__that, const char *__s)); | |
506 | +static void merge_comment_dot PARAMS ((po_ty *__that, const char *__s)); | |
507 | +static void merge_comment_special PARAMS ((po_ty *__that, const char *__s)); | |
508 | +static void merge_comment_filepos PARAMS ((po_ty *__that, const char *__name, | |
509 | + int __line)); | |
510 | +static message_list_ty *grammar PARAMS ((const char *__filename)); | |
c155f01f | 511 | +static message_list_ty *append PARAMS ((const char *__fn1, const char *__fn2, int diff_only)); |
fb4f7b36 | 512 | +static message_list_ty *merge PARAMS ((const char *__fn1, const char *__fn2)); |
513 | +static message_list_ty *invert PARAMS ((const char *__fn)); | |
514 | +static message_list_ty *missing PARAMS ((const char *__fn)); | |
515 | +static message_list_ty *un_duplicate PARAMS ((message_list_ty *dup_list)); | |
516 | +static message_list_ty *empty PARAMS ((const char *__fn)); | |
c155f01f | 517 | +static message_list_ty *dummy PARAMS ((const char *__fn)); |
fb4f7b36 | 518 | +static message_list_ty *split PARAMS ((const char *__fn, int __flag)); |
519 | +static message_list_ty *explode PARAMS ((const char *__fn, int __flag)); | |
520 | +static message_list_ty *master PARAMS ((message_list_ty **prev, const char *__fn, | |
521 | + const char *__master_id)); | |
522 | +static message_ty *message_join PARAMS ((message_ty *def, message_ty *ref)); | |
c155f01f | 523 | +static struct message_ty *new_header PARAMS((void)); |
fb4f7b36 | 524 | + |
525 | +/* What kind of actions we can make */ | |
526 | +#define ACTION_DO_SPLIT 1 | |
527 | +#define ACTION_DO_INVERT 2 | |
528 | +#define ACTION_DO_MERGE 3 | |
529 | +#define ACTION_DO_EMPTY 4 | |
530 | +#define ACTION_DO_EXPLODE 5 | |
531 | +#define ACTION_DO_MASTER 6 | |
532 | +#define ACTION_DO_SORT 7 | |
533 | +#define ACTION_DO_APPEND 8 | |
534 | +#define ACTION_DO_UNDUP 9 | |
535 | +#define ACTION_DO_MISSING 10 | |
c155f01f | 536 | +#define ACTION_DO_DUMMY 11 |
537 | +#define ACTION_DO_DIFF 12 | |
fb4f7b36 | 538 | + |
539 | +int main (argc, argv) | |
540 | + int argc; | |
541 | + char **argv; | |
542 | +{ | |
543 | + int opt; | |
544 | + int do_action = 0; | |
545 | + char *output_file; | |
546 | + message_list_ty *result; | |
547 | + | |
548 | + /* Long options. */ | |
549 | + const struct option long_options[] = | |
550 | + { | |
551 | + /* command pairs */ | |
552 | + { "split-id", no_argument, &split_flag, SPLIT_MSGID }, | |
553 | + { "split-str", no_argument, &split_flag, SPLIT_MSGSTR }, | |
554 | + | |
555 | + { "explode-id", required_argument, &explode_flag, EXPLODE_MSGID }, | |
556 | + { "explode-str", required_argument, &explode_flag, EXPLODE_MSGSTR }, | |
557 | + | |
558 | + { "sort-by-file", no_argument, &sort_order, SORT_BY_FILEPOS }, | |
559 | + { "sort-by-id", no_argument, &sort_order, SORT_BY_MSGID }, | |
560 | + | |
561 | + /* flags */ | |
c155f01f | 562 | + { "fuzzy", no_argument, &fuzzy, 1 }, |
563 | + { "force-po", no_argument, &force_po, 1 }, | |
564 | + { "width", required_argument, NULL, 'w', }, | |
565 | + { "output-file", required_argument, NULL, 'o' }, | |
566 | + { "useless", no_argument, &useless, 1 }, | |
fb4f7b36 | 567 | + |
c155f01f | 568 | + /* |
569 | + * ACTIONS | |
570 | + */ | |
571 | + /* switch the msgid and the msgstr strings in a PO file */ | |
fb4f7b36 | 572 | + { "invert", no_argument, &do_action, ACTION_DO_INVERT }, |
c155f01f | 573 | + |
574 | + /* fill the msgstr with the contents of msgid */ | |
575 | + { "dummy", no_argument, &do_action, ACTION_DO_DUMMY }, | |
576 | + | |
577 | + /* merge two files based on commin msgid. The msgid is stored as a comment */ | |
fb4f7b36 | 578 | + { "merge", no_argument, &do_action, ACTION_DO_MERGE }, |
c155f01f | 579 | + |
580 | + /* strip out the msgstr creating a POT form a PO file */ | |
fb4f7b36 | 581 | + { "empty", no_argument, &do_action, ACTION_DO_EMPTY }, |
c155f01f | 582 | + |
583 | + /* combine sevaral PO files into a "master" catalog */ | |
fb4f7b36 | 584 | + { "master", no_argument, &do_action, ACTION_DO_MASTER }, |
c155f01f | 585 | + |
586 | + /* append entries from ref POT that do not exist in file PO */ | |
fb4f7b36 | 587 | + { "append", no_argument, &do_action, ACTION_DO_APPEND }, |
c155f01f | 588 | + |
589 | + /* show only stuff from ref POT that does not exist in file PO */ | |
590 | + { "diff", no_argument, &do_action, ACTION_DO_DIFF }, | |
591 | + | |
592 | + /* ommit comments for the 'append' and 'diff' */ | |
593 | + { "comments", no_argument, &comments, 1 }, | |
594 | + | |
595 | + /* remove duplicates */ | |
fb4f7b36 | 596 | + { "unduplicate", no_argument, &do_action, ACTION_DO_UNDUP }, |
c155f01f | 597 | + |
598 | + /* output strings that need translation (fuzzies and untranslated */ | |
fb4f7b36 | 599 | + { "missing", no_argument, &do_action, ACTION_DO_MISSING }, |
600 | + | |
c155f01f | 601 | + /* output strings that need translation (fuzzies and untranslated */ |
602 | + { "missing", no_argument, &do_action, ACTION_DO_MISSING }, | |
603 | + | |
604 | + /* INFO */ | |
fb4f7b36 | 605 | + { "help", no_argument, NULL, 'h' }, |
606 | + { "version", no_argument, NULL, 'V' }, | |
607 | + | |
608 | + { NULL, 0, NULL, 0 } | |
609 | + }; | |
610 | + | |
611 | + | |
612 | + /* Set program name for messages. */ | |
613 | + program_name = argv[0]; | |
614 | + verbosity_level = 0; | |
615 | + error_print_progname = error_print; | |
616 | + gram_max_allowed_errors = INT_MAX; | |
617 | + | |
618 | +#ifdef HAVE_SETLOCALE | |
619 | + /* Set locale via LC_ALL. */ | |
620 | + setlocale (LC_ALL, ""); | |
621 | +#endif | |
622 | + | |
623 | + /* Set the text message domain. */ | |
624 | + bindtextdomain (PACKAGE, LOCALEDIR); | |
625 | + textdomain (PACKAGE); | |
626 | + | |
627 | + /* Set default values for variables. */ | |
628 | + output_file = NULL; | |
629 | + | |
630 | + while ((opt | |
631 | + = getopt_long (argc, argv, "ho:Vw:v", long_options, NULL)) | |
632 | + != EOF) | |
633 | + switch (opt) { | |
634 | + case '\0': /* Long option. */ | |
635 | + break; | |
636 | + | |
637 | + case 'h': | |
638 | + /* Help is requested. */ | |
639 | + usage (EXIT_SUCCESS); | |
640 | + break; | |
641 | + | |
642 | + case 'o': | |
643 | + output_file = optarg; | |
644 | + break; | |
645 | + | |
646 | + case 'v': | |
647 | + ++verbosity_level; | |
648 | + break; | |
649 | + | |
650 | + case 'V': | |
651 | + /* Version information is requested. */ | |
652 | + printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION); | |
653 | + /* xgettext: no-wrap */ | |
654 | + printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\ | |
655 | +This is free software; see the source for copying conditions. There is NO\n\ | |
656 | +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\ | |
657 | +"), "1995, 1996, 1997, 1998"); | |
658 | + printf (_("Written by %s.\n"), "Cristian Gafton"); | |
659 | + exit (EXIT_SUCCESS); | |
660 | + break; | |
661 | + | |
662 | + case 'w': | |
663 | + { | |
664 | + int value; | |
665 | + char *endp; | |
666 | + value = strtol (optarg, &endp, 10); | |
667 | + if (endp != optarg) | |
668 | + message_page_width_set (value); | |
669 | + } | |
670 | + break; | |
671 | + | |
672 | + default: | |
673 | + usage (EXIT_FAILURE); | |
674 | + break; | |
675 | + } | |
676 | + | |
677 | + /* Test whether we have an .po file name as argument. */ | |
678 | + if (optind >= argc) { | |
679 | + error (EXIT_SUCCESS, 0, _("no input files given")); | |
680 | + usage (EXIT_FAILURE); | |
681 | + } | |
682 | + | |
683 | + /* transform one of the pair command into a real action */ | |
684 | + if (do_action == 0 && split_flag > 0) | |
685 | + do_action = ACTION_DO_SPLIT; | |
686 | + if (do_action == 0 && explode_flag > 0) | |
687 | + do_action = ACTION_DO_EXPLODE; | |
688 | + if (do_action == 0 && sort_order != 0) | |
689 | + do_action = ACTION_DO_SORT; | |
690 | + | |
691 | + /* okay, we have our file in argv[optind]*/ | |
692 | + /* what should we do */ | |
693 | + switch (do_action) { | |
694 | + case ACTION_DO_INVERT: | |
695 | + /* we are asked to invert the msgid and msgstr strings in a .po file */ | |
696 | + result = invert (argv[optind]); | |
697 | + break; | |
698 | + case ACTION_DO_EMPTY: | |
699 | + /* we are asked to delete the msgstr text in a .po file */ | |
700 | + result = empty (argv[optind]); | |
701 | + break; | |
c155f01f | 702 | + case ACTION_DO_DUMMY: |
703 | + /* dummy out a POT file in a PO file (msgstr = msgid) */ | |
704 | + result = dummy (argv[optind]); | |
705 | + break; | |
fb4f7b36 | 706 | + case ACTION_DO_SPLIT: |
707 | + /* we are asked to split the file into corresponding meta po files */ | |
708 | + result = split(argv[optind], split_flag); | |
709 | + /* Whatever we are asked to split out */ | |
710 | + break; | |
711 | + case ACTION_DO_EXPLODE: | |
712 | + /* we are asked to generate a single meta entry for each xref */ | |
713 | + result = explode(argv[optind], explode_flag); | |
714 | + break; | |
715 | + case ACTION_DO_MERGE: | |
716 | + /* we gave to merge (id1,str1) and (id2,str2) into (str1,str2) */ | |
717 | + if (optind + 2 != argc) { | |
718 | + error (EXIT_SUCCESS, 0, _("exactly 2 input files required for merge")); | |
719 | + usage (EXIT_FAILURE); | |
720 | + } | |
721 | + result = merge (argv[optind], argv[optind+1]); | |
722 | + break; | |
c155f01f | 723 | + case ACTION_DO_DIFF: |
fb4f7b36 | 724 | + case ACTION_DO_APPEND: |
725 | + /* append all (id2,str2) entries from second file to the first one | |
726 | + * provided that a id2 msgid is not already present there */ | |
727 | + if (optind + 2 != argc) { | |
728 | + error (EXIT_SUCCESS, 0, _("exactly 2 input files required for append")); | |
729 | + usage (EXIT_FAILURE); | |
730 | + } | |
c155f01f | 731 | + result = append (argv[optind], argv[optind+1], do_action == ACTION_DO_DIFF); |
fb4f7b36 | 732 | + break; |
733 | + case ACTION_DO_UNDUP: | |
734 | + /* read in the message catalog and try to colapse | |
735 | + the duplicate messageids together */ | |
736 | + /* Read in the file */ | |
737 | + result = grammar (argv[optind]); | |
738 | + result = un_duplicate(result); | |
739 | + break; | |
740 | + case ACTION_DO_MASTER: | |
741 | + /* This is a little bit funkier: | |
742 | + * Suppose in A.po we have: | |
743 | + * msgid "id" | |
744 | + * msgstr "str1" | |
745 | + * And in B.po we have: | |
746 | + * msgid "id" | |
747 | + * msgstr "str2" | |
748 | + * then the result will look like: | |
749 | + * msgid "id" | |
750 | + * msgstr(A) "str1" | |
751 | + * msgstr(B) "str2" | |
752 | + */ | |
753 | + result = NULL; /* Start from scratch */ | |
754 | + for (opt = optind ; opt < argc ; opt++) { | |
755 | + char *file_name, *master_id; | |
756 | + char *tmp; | |
757 | + /* get the file name and the master id */ | |
758 | + file_name = strdup(argv[opt]); | |
759 | + master_id = strdup(basename(argv[opt])); | |
760 | + tmp = rindex(master_id, '.'); | |
761 | + if (tmp == NULL) { | |
762 | + error (EXIT_SUCCESS, 0, | |
763 | + _("Message catalog %s does not have a valid name"), argv[opt]); | |
764 | + usage (EXIT_FAILURE); | |
765 | + } | |
766 | + *tmp = '\0'; | |
767 | + result = master(&result, file_name, master_id); | |
768 | + } | |
769 | + master_list_print(result, output_file); | |
770 | + return EXIT_SUCCESS; | |
771 | + break; | |
772 | + case ACTION_DO_SORT: | |
773 | + /* we'll just read in the file and let the sort | |
774 | + * statements later deal with it */ | |
775 | + result = grammar (argv[optind]); | |
776 | + break; | |
777 | + case ACTION_DO_MISSING: | |
778 | + /* generate a new po file of only the missing and non-translated strings */ | |
779 | + result = missing (argv[optind]); | |
780 | + break; | |
781 | + default: | |
782 | + error (EXIT_SUCCESS, 0, _("Unknown Action")); | |
783 | + exit(EXIT_FAILURE); | |
784 | + } | |
785 | + | |
786 | + /* Sort the results. */ | |
787 | + switch (sort_order) { | |
788 | + case SORT_BY_FILEPOS: | |
789 | + message_list_sort_by_filepos (result); | |
790 | + break; | |
791 | + case SORT_BY_MSGID: | |
792 | + message_list_sort_by_msgid (result); | |
793 | + break; | |
794 | + } | |
795 | + | |
796 | + /* Write the merged message list out. */ | |
797 | + message_list_print (result, output_file, force_po, 0); | |
798 | + | |
799 | + exit (EXIT_SUCCESS); | |
800 | +} | |
801 | + | |
802 | + | |
803 | +/* Display usage information and exit. */ | |
804 | +static void | |
805 | +usage (status) | |
806 | + int status; | |
807 | +{ | |
808 | + if (status != EXIT_SUCCESS) | |
809 | + fprintf (stderr, _("Try `%s --help' for more information.\n"), | |
810 | + program_name); | |
811 | + else { | |
812 | + /* xgettext: no-wrap */ | |
813 | + printf (_("\ | |
814 | +Usage: %s [OPTION] file.po [ref.po]\n\ | |
815 | +Mandatory arguments to long options are mandatory for short options too.\n\ | |
816 | + -h, --help display this help and exit\n\ | |
817 | + --invert invert a po file by switching msgid and msgstr\n\ | |
818 | + -o, --output-file=FILE result will be written to FILE\n\ | |
819 | + -v, --verbose increase verbosity level\n\ | |
820 | + -V, --version output version information and exit\n\ | |
821 | + -w, --width=NUMBER set output page width\n\ | |
822 | + --sort-by-file sort by file in which the msgs appear\n\ | |
823 | + --sort-by-id sort by message ids\n\ | |
824 | + --split-id split msgid from message catalg into meta catalog\n\ | |
825 | + --split-str split msgstr from message catalg into meta catalog\n\ | |
826 | + --merge merge file.po and ref.po based on the common msgid\n\ | |
c155f01f | 827 | + + --useless also generate entries for unmatched pairs.\n\ |
fb4f7b36 | 828 | + --master join any number of files in a master-formatted catalog\n\ |
829 | + + --fuzzy use the fuzzy entries when merging in PO files.\n\ | |
830 | + --empty empty the contents of the .po file, creating a .pot\n\ | |
c155f01f | 831 | + --dummy create a .po file from a .pot by doing msgstr = msgid\n\ |
fb4f7b36 | 832 | + --append append entries from ref.po that don't exist in file.po\n\ |
c155f01f | 833 | + --diff show what entries from ref.po don't exist in file.po\n\ |
834 | + + --comments add comments from ref.po for existing entries. in file.po.\n\ | |
fb4f7b36 | 835 | + --explode-id generate a single meta msgid entry for each xref in file.po\n\ |
836 | + --explode-str generate a single meta msgstr entry for each xref in file.po\n\ | |
837 | + --unduplicate colapse together duplicate message ids. (not working currently)\n\ | |
838 | + --missing output only the fuzzy and empty message strings\n\ | |
839 | +\n\ | |
840 | +This program can be used to alter .po files in ways no sane mind would think about.\n\ | |
841 | +Report problems to <gafton@redhat.com>\n\ | |
842 | +"), program_name); | |
843 | + } | |
844 | + exit (status); | |
845 | +} | |
846 | + | |
847 | + | |
848 | +/* The address of this function will be assigned to the hook in the | |
849 | + error functions. */ | |
850 | +static void error_print () | |
851 | +{ | |
852 | + /* We don't want the program name to be printed in messages. Emacs' | |
853 | + compile.el does not like this. */ | |
854 | + | |
855 | + /* FIXME Why must this program toady to Emacs? Why can't compile.el | |
856 | + be enhanced to cope with a leading program name? --PMiller */ | |
857 | +} | |
858 | + | |
859 | + | |
860 | +static void | |
861 | +merge_constructor (that) | |
862 | + po_ty *that; | |
863 | +{ | |
864 | + merge_class_ty *this = (merge_class_ty *) that; | |
865 | + | |
866 | + this->mlp = message_list_alloc (); | |
867 | + this->domain = MESSAGE_DOMAIN_DEFAULT; | |
868 | + this->domain_list = string_list_alloc (); | |
869 | + this->comment = NULL; | |
870 | + this->comment_dot = NULL; | |
871 | + this->filepos_count = 0; | |
872 | + this->filepos = NULL; | |
873 | + this->is_fuzzy = 0; | |
874 | + this->is_c_format = undecided; | |
875 | + this->do_wrap = undecided; | |
876 | +} | |
877 | + | |
878 | + | |
879 | +static void | |
880 | +merge_destructor (that) | |
881 | + po_ty *that; | |
882 | +{ | |
883 | + merge_class_ty *this = (merge_class_ty *) that; | |
884 | + size_t j; | |
885 | + | |
886 | + string_list_free (this->domain_list); | |
887 | + /* Do not free this->mlp. */ | |
888 | + if (this->comment != NULL) | |
889 | + string_list_free (this->comment); | |
890 | + if (this->comment_dot != NULL) | |
891 | + string_list_free (this->comment_dot); | |
892 | + for (j = 0; j < this->filepos_count; ++j) | |
893 | + free (this->filepos[j].file_name); | |
894 | + if (this->filepos != NULL) | |
895 | + free (this->filepos); | |
896 | +} | |
897 | + | |
898 | + | |
899 | +static void | |
900 | +merge_directive_domain (that, name) | |
901 | + po_ty *that; | |
902 | + char *name; | |
903 | +{ | |
904 | + size_t j; | |
905 | + | |
906 | + merge_class_ty *this = (merge_class_ty *) that; | |
907 | + /* Override current domain name. Don't free memory. */ | |
908 | + this->domain = name; | |
909 | + | |
910 | + /* If there are accumulated comments, throw them away, they are | |
911 | + probably part of the file header, or about the domain directive, | |
912 | + and will be unrelated to the next message. */ | |
913 | + if (this->comment != NULL) | |
914 | + { | |
915 | + string_list_free (this->comment); | |
916 | + this->comment = NULL; | |
917 | + } | |
918 | + if (this->comment_dot != NULL) | |
919 | + { | |
920 | + string_list_free (this->comment_dot); | |
921 | + this->comment_dot = NULL; | |
922 | + } | |
923 | + for (j = 0; j < this->filepos_count; ++j) | |
924 | + free (this->filepos[j].file_name); | |
925 | + if (this->filepos != NULL) | |
926 | + free (this->filepos); | |
927 | + this->filepos_count = 0; | |
928 | + this->filepos = NULL; | |
929 | +} | |
930 | + | |
931 | + | |
932 | +static void | |
933 | +merge_directive_message (that, msgid, msgid_pos, msgstr, msgstr_pos) | |
934 | + po_ty *that; | |
935 | + char *msgid; | |
936 | + lex_pos_ty *msgid_pos; | |
937 | + char *msgstr; | |
938 | + lex_pos_ty *msgstr_pos; | |
939 | +{ | |
940 | + merge_class_ty *this = (merge_class_ty *) that; | |
941 | + message_ty *mp; | |
942 | + message_variant_ty *mvp; | |
943 | + size_t j; | |
944 | + | |
945 | + /* Remember the domain names for later. */ | |
946 | + string_list_append_unique (this->domain_list, this->domain); | |
947 | + | |
948 | + /* See if this message ID has been seen before. */ | |
949 | + mp = message_list_search (this->mlp, msgid); | |
950 | + if (mp) | |
951 | + free (msgid); | |
952 | + else | |
953 | + { | |
954 | + mp = message_alloc (msgid); | |
955 | + message_list_append (this->mlp, mp); | |
956 | + } | |
957 | + | |
958 | + /* Add the accumulated comments to the message. Clear the | |
959 | + accumulation in preparation for the next message. */ | |
960 | + if (this->comment != NULL) | |
961 | + { | |
962 | + for (j = 0; j < this->comment->nitems; ++j) | |
963 | + message_comment_append (mp, this->comment->item[j]); | |
964 | + string_list_free (this->comment); | |
965 | + this->comment = NULL; | |
966 | + } | |
967 | + if (this->comment_dot != NULL) | |
968 | + { | |
969 | + for (j = 0; j < this->comment_dot->nitems; ++j) | |
970 | + message_comment_dot_append (mp, this->comment_dot->item[j]); | |
971 | + string_list_free (this->comment_dot); | |
972 | + this->comment_dot = NULL; | |
973 | + } | |
974 | + for (j = 0; j < this->filepos_count; ++j) | |
975 | + { | |
976 | + lex_pos_ty *pp; | |
977 | + | |
978 | + pp = &this->filepos[j]; | |
979 | + message_comment_filepos (mp, pp->file_name, pp->line_number); | |
980 | + free (pp->file_name); | |
981 | + } | |
982 | + mp->is_fuzzy = this->is_fuzzy; | |
983 | + mp->is_c_format = this->is_c_format; | |
984 | + mp->do_wrap = this->do_wrap; | |
985 | + | |
986 | + if (this->filepos != NULL) | |
987 | + free (this->filepos); | |
988 | + this->filepos_count = 0; | |
989 | + this->filepos = NULL; | |
990 | + this->is_fuzzy = 0; | |
991 | + this->is_c_format = undecided; | |
992 | + this->do_wrap = undecided; | |
993 | + | |
994 | + /* See if this domain has been seen for this message ID. */ | |
995 | + mvp = message_variant_search (mp, this->domain); | |
996 | + if (mvp) | |
997 | + { | |
998 | + gram_error_at_line (msgid_pos, _("duplicate message definition")); | |
999 | + gram_error_at_line (&mvp->pos, _("\ | |
1000 | +...this is the location of the first definition")); | |
1001 | + free (msgstr); | |
1002 | + } | |
1003 | + else | |
1004 | + message_variant_append (mp, this->domain, msgstr, msgstr_pos); | |
1005 | +} | |
1006 | + | |
1007 | + | |
1008 | +static void | |
1009 | +merge_parse_brief (that) | |
1010 | + po_ty *that; | |
1011 | +{ | |
1012 | + po_lex_pass_comments (1); | |
1013 | +} | |
1014 | + | |
1015 | + | |
1016 | +static void | |
1017 | +merge_parse_debrief (that) | |
1018 | + po_ty *that; | |
1019 | +{ | |
1020 | + merge_class_ty *this = (merge_class_ty *) that; | |
1021 | + message_list_ty *mlp = this->mlp; | |
1022 | + size_t j; | |
1023 | + | |
1024 | + /* For each domain in the used-domain-list, make sure each message | |
1025 | + defines a msgstr in that domain. */ | |
1026 | + for (j = 0; j < this->domain_list->nitems; ++j) | |
1027 | + { | |
1028 | + const char *domain_name; | |
1029 | + size_t k; | |
1030 | + | |
1031 | + domain_name = this->domain_list->item[j]; | |
1032 | + for (k = 0; k < mlp->nitems; ++k) | |
1033 | + { | |
1034 | + const message_ty *mp; | |
1035 | + size_t m; | |
1036 | + | |
1037 | + mp = mlp->item[k]; | |
1038 | + for (m = 0; m < mp->variant_count; ++m) | |
1039 | + { | |
1040 | + message_variant_ty *mvp; | |
1041 | + | |
1042 | + mvp = &mp->variant[m]; | |
1043 | + if (strcmp (domain_name, mvp->domain) == 0) | |
1044 | + break; | |
1045 | + } | |
1046 | + if (m >= mp->variant_count) | |
1047 | + gram_error_at_line (&mp->variant[0].pos, _("\ | |
1048 | +this message has no definition in the \"%s\" domain"), domain_name); | |
1049 | + } | |
1050 | + } | |
1051 | +} | |
1052 | + | |
1053 | + | |
1054 | +static void | |
1055 | +merge_comment (that, s) | |
1056 | + po_ty *that; | |
1057 | + const char *s; | |
1058 | +{ | |
1059 | + merge_class_ty *this = (merge_class_ty *) that; | |
1060 | + | |
1061 | + if (this->comment == NULL) | |
1062 | + this->comment = string_list_alloc (); | |
1063 | + string_list_append (this->comment, s); | |
1064 | +} | |
1065 | + | |
1066 | + | |
1067 | +static void | |
1068 | +merge_comment_dot (that, s) | |
1069 | + po_ty *that; | |
1070 | + const char *s; | |
1071 | +{ | |
1072 | + merge_class_ty *this = (merge_class_ty *) that; | |
1073 | + | |
1074 | + if (this->comment_dot == NULL) | |
1075 | + this->comment_dot = string_list_alloc (); | |
1076 | + string_list_append (this->comment_dot, s); | |
1077 | +} | |
1078 | + | |
1079 | + | |
1080 | +static void | |
1081 | +merge_comment_special (that, s) | |
1082 | + po_ty *that; | |
1083 | + const char *s; | |
1084 | +{ | |
1085 | + merge_class_ty *this = (merge_class_ty *) that; | |
1086 | + | |
1087 | + if (strstr (s, "fuzzy") != NULL) | |
1088 | + this->is_fuzzy = 1; | |
1089 | + | |
1090 | + this->is_c_format = parse_c_format_description_string (s); | |
1091 | + this->do_wrap = parse_c_width_description_string (s); | |
1092 | +} | |
1093 | + | |
1094 | + | |
1095 | +static void | |
1096 | +merge_comment_filepos (that, name, line) | |
1097 | + po_ty *that; | |
1098 | + const char *name; | |
1099 | + int line; | |
1100 | +{ | |
1101 | + merge_class_ty *this = (merge_class_ty *) that; | |
1102 | + size_t nbytes; | |
1103 | + lex_pos_ty *pp; | |
1104 | + | |
1105 | + if (!line_comment) | |
1106 | + return; | |
1107 | + nbytes = (this->filepos_count + 1) * sizeof (this->filepos[0]); | |
1108 | + this->filepos = xrealloc (this->filepos, nbytes); | |
1109 | + pp = &this->filepos[this->filepos_count++]; | |
1110 | + pp->file_name = xstrdup (name); | |
1111 | + pp->line_number = line; | |
1112 | +} | |
1113 | + | |
1114 | + | |
1115 | +/* So that the one parser can be used for multiple programs, and also | |
1116 | + use good data hiding and encapsulation practices, an object | |
1117 | + oriented approach has been taken. An object instance is allocated, | |
1118 | + and all actions resulting from the parse will be through | |
1119 | + invokations of method functions of that object. */ | |
1120 | + | |
1121 | +static po_method_ty merge_methods = | |
1122 | +{ | |
1123 | + sizeof (merge_class_ty), | |
1124 | + merge_constructor, | |
1125 | + merge_destructor, | |
1126 | + merge_directive_domain, | |
1127 | + merge_directive_message, | |
1128 | + merge_parse_brief, | |
1129 | + merge_parse_debrief, | |
1130 | + merge_comment, | |
1131 | + merge_comment_dot, | |
1132 | + merge_comment_filepos, | |
1133 | + merge_comment_special | |
1134 | +}; | |
1135 | + | |
1136 | + | |
1137 | +static message_list_ty * | |
1138 | +grammar (filename) | |
1139 | + const char *filename; | |
1140 | +{ | |
1141 | + po_ty *pop; | |
1142 | + message_list_ty *mlp; | |
1143 | + | |
1144 | + pop = po_alloc (&merge_methods); | |
1145 | + po_lex_pass_obsolete_entries (1); | |
1146 | + po_scan (pop, filename); | |
1147 | + mlp = ((merge_class_ty *) pop)->mlp; | |
1148 | + po_free (pop); | |
1149 | + return mlp; | |
1150 | +} | |
1151 | + | |
1152 | + | |
1153 | +#define DOT_FREQUENCE 10 | |
1154 | + | |
1155 | +/* | |
1156 | + * Given two meta files (m,X) and (m,N) generate a po file (N,X) | |
1157 | + * preserving the meta information in special comments | |
1158 | + */ | |
1159 | +static message_list_ty * | |
1160 | +merge (fn1, fn2) | |
1161 | + const char *fn1; /* definitions */ | |
1162 | + const char *fn2; /* references */ | |
1163 | +{ | |
1164 | + message_list_ty *def; | |
1165 | + message_list_ty *ref; | |
1166 | + message_ty *defmsg; | |
1167 | + size_t j, k; | |
1168 | + size_t merged, fuzzied, missing, obsolete; | |
1169 | + message_list_ty *result; | |
1170 | + | |
1171 | + /* This is the definitions file, created by a human. */ | |
1172 | + def = grammar (fn1); | |
1173 | + | |
1174 | + /* This is the references file, created by groping the sources with | |
1175 | + * the xgettext program. */ | |
1176 | + ref = grammar (fn2); | |
1177 | + | |
1178 | + result = message_list_alloc (); | |
1179 | + | |
1180 | + /* Every reference must be matched with its definition. */ | |
1181 | + for (j = 0; j < ref->nitems; ++j) { | |
1182 | + message_ty *refmsg; | |
1183 | + | |
1184 | + /* Because merging can take a while we print something to signal | |
1185 | + * we are not dead. */ | |
1186 | + if (verbosity_level >= 1 && j % DOT_FREQUENCE == 0) | |
1187 | + fputc ('.', stderr); | |
1188 | + | |
1189 | + refmsg = ref->item[j]; | |
1190 | + | |
1191 | + /* See if it is in the other file. */ | |
1192 | + defmsg = message_list_search (def, refmsg->msgid); | |
1193 | + if (defmsg) { | |
1194 | + /* Merge the reference with the definition: take the #. and | |
1195 | + #: comments from the reference, take the # comments from | |
1196 | + the definition, take the msgstr from the definition. Add | |
1197 | + this merged entry to the output message list. */ | |
1198 | + message_ty *mp = message_join (defmsg, refmsg); | |
1199 | + | |
c155f01f | 1200 | + if (mp != NULL) |
1201 | + message_list_append (result, mp); | |
fb4f7b36 | 1202 | + |
1203 | + /* Remember that this message has been used, when we scan | |
1204 | + later to see if anything was omitted. */ | |
1205 | + defmsg->used = 1; | |
1206 | + ++merged; | |
1207 | + continue; | |
c155f01f | 1208 | + } else if (useless) { |
fb4f7b36 | 1209 | + message_ty *mp; |
1210 | + const char *tmp; | |
1211 | + if (verbosity_level > 1) | |
1212 | + gram_error_at_line (&refmsg->variant[0].pos, | |
1213 | + _("this message is used but not defined in %s"), | |
1214 | + fn1); | |
1215 | + | |
1216 | + /* Insert the blank record */ | |
1217 | + mp = message_copy (refmsg); | |
1218 | + tmp = mp->msgid; | |
1219 | + mp->msgid = mp->variant[0].msgstr; | |
1220 | + mp->variant[0].msgstr = ""; | |
1221 | + message_list_append (result, mp); | |
1222 | + ++missing; | |
1223 | + } | |
1224 | + } | |
1225 | + | |
1226 | + /* Look for messages in the definition file, which are not present | |
1227 | + in the reference file, indicating messages which defined but not | |
1228 | + used in the program. */ | |
c155f01f | 1229 | + if (useless) |
1230 | + for (k = 0; k < def->nitems; ++k) { | |
1231 | + defmsg = def->item[k]; | |
1232 | + if (defmsg->used) | |
1233 | + continue; | |
fb4f7b36 | 1234 | + |
c155f01f | 1235 | + /* Remember the old translation although it is not used anymore. |
1236 | + But we mark it as obsolete. */ | |
1237 | + defmsg->obsolete = 1; | |
fb4f7b36 | 1238 | + |
c155f01f | 1239 | + message_list_append (result, defmsg); |
1240 | + ++obsolete; | |
1241 | + } | |
fb4f7b36 | 1242 | + |
1243 | + /* Report some statistics. */ | |
1244 | + if (verbosity_level > 0) | |
1245 | + fprintf (stderr, | |
1246 | + _("%sRead %d old + %d reference, merged %d, fuzzied %d, missing %d, obsolete %d.\n"), | |
1247 | + verbosity_level >= 1 ? "\n" : "", | |
1248 | + def->nitems, ref->nitems, merged, fuzzied, missing, obsolete); | |
1249 | + else if (verbosity_level > 0) | |
1250 | + fputs (_(" done.\n"), stderr); | |
1251 | + | |
1252 | + /* get rid of the duplicate message ids */ | |
1253 | + return un_duplicate(result); | |
1254 | +} | |
1255 | + | |
1256 | +/* | |
1257 | + * Given two meta files f1 and f2 append to f1 the entries from f2 that | |
1258 | + * are not found in f1. First f1 is read in, the for each entry in f2 we | |
1259 | + * try to locate it in f1; if it is not there already we add it. | |
1260 | + */ | |
1261 | +static message_list_ty * | |
c155f01f | 1262 | +append (fn1, fn2, diff_only) |
fb4f7b36 | 1263 | + const char *fn1; /* base file */ |
1264 | + const char *fn2; /* more stuff found in here */ | |
c155f01f | 1265 | + int diff_only; /* do we want only the diffs? */ |
fb4f7b36 | 1266 | +{ |
1267 | + message_list_ty *ml1; | |
1268 | + message_list_ty *ml2; | |
c155f01f | 1269 | + message_list_ty *result; |
fb4f7b36 | 1270 | + size_t j; |
1271 | + size_t added = 0; | |
1272 | + int initial; | |
1273 | + | |
1274 | + /* This is the definitions file, created by a human. */ | |
1275 | + ml1 = grammar (fn1); | |
1276 | + initial = ml1->nitems; | |
1277 | + | |
1278 | + /* This is the references file, created by groping the sources with | |
1279 | + * the xgettext program. */ | |
1280 | + ml2 = grammar (fn2); | |
1281 | + | |
c155f01f | 1282 | + result = message_list_alloc(); |
1283 | + | |
fb4f7b36 | 1284 | + /* Every reference must be matched with its definition. */ |
1285 | + for (j = 0; j < ml2->nitems; ++j) { | |
1286 | + message_ty *oldmsg; | |
1287 | + message_ty *newmsg; | |
1288 | + int i; | |
1289 | + | |
1290 | + /* Because merging can take a while we print something to signal | |
1291 | + * we are not dead. */ | |
1292 | + if (verbosity_level >= 1 && j % DOT_FREQUENCE == 0) | |
1293 | + fputc ('.', stderr); | |
1294 | + | |
1295 | + /* this is the message we want to add */ | |
1296 | + newmsg = ml2->item[j]; | |
1297 | + | |
1298 | + /* See if it is in the other file. */ | |
1299 | + oldmsg = message_list_search (ml1, newmsg->msgid); | |
1300 | + if (oldmsg == NULL) { /* not found, stick it in */ | |
c155f01f | 1301 | + message_list_append (result, newmsg); |
fb4f7b36 | 1302 | + newmsg->used = 1; |
1303 | + ++added; | |
1304 | + continue; | |
1305 | + } | |
c155f01f | 1306 | + |
1307 | + /* else, if it is found, add the coments from it if we want them */ | |
1308 | + if (comments != 0) { | |
1309 | + if (newmsg->comment) | |
1310 | + for (i = 0; i < newmsg->comment->nitems; ++i) | |
1311 | + message_comment_append (oldmsg, newmsg->comment->item[i]); | |
fb4f7b36 | 1312 | + |
c155f01f | 1313 | + if (newmsg->comment_dot) |
1314 | + for (i = 0; i < newmsg->comment_dot->nitems; ++i) | |
1315 | + message_comment_dot_append (oldmsg, newmsg->comment_dot->item[i]); | |
fb4f7b36 | 1316 | + |
c155f01f | 1317 | + /* file position comments */ |
1318 | + for (i = 0; i < newmsg->filepos_count; ++i) { | |
1319 | + lex_pos_ty *pp = &newmsg->filepos[i]; | |
1320 | + message_comment_filepos (oldmsg, pp->file_name, pp->line_number); | |
1321 | + } | |
fb4f7b36 | 1322 | + } |
1323 | + newmsg->used++; | |
1324 | + } | |
1325 | + | |
1326 | + /* Report some statistics. */ | |
1327 | + if (verbosity_level > 0) | |
1328 | + fprintf (stderr, | |
1329 | + _("%sRead %d file1 + %d file2, added %d\n"), | |
1330 | + verbosity_level >= 1 ? "\n" : "", | |
1331 | + initial, ml2->nitems, added); | |
1332 | + else if (verbosity_level > 0) | |
1333 | + fputs (_(" done.\n"), stderr); | |
1334 | + | |
c155f01f | 1335 | + /* do we want only the diffs? */ |
1336 | + if (diff_only) { | |
1337 | + return result; | |
1338 | + } | |
1339 | + | |
1340 | + /* append the result to ml1 */ | |
1341 | + for (j = 0; j < result->nitems; ++j) | |
1342 | + message_list_append(ml1, result->item[j]); | |
1343 | + | |
1344 | + /* No need to free the result since message_list_alloc copies only the | |
1345 | + references over */ | |
fb4f7b36 | 1346 | + return ml1; |
1347 | +} | |
1348 | + | |
1349 | +static message_list_ty * | |
1350 | +master (prev, fn, master_id) | |
1351 | + message_list_ty **prev; /* The result of what we had before */ | |
1352 | + const char *fn; /* file name */ | |
1353 | + const char *master_id; /* what to put as msgstr(master_id) */ | |
1354 | +{ | |
1355 | + message_list_ty *list; | |
1356 | + message_list_ty *result; | |
1357 | + int j; | |
1358 | + | |
1359 | + /* Read in the file */ | |
1360 | + list = grammar (fn); | |
1361 | + | |
1362 | + if (*prev == NULL) | |
1363 | + *prev = message_list_alloc (); | |
1364 | + | |
1365 | + result = *prev; /* Work with that */ | |
1366 | + | |
1367 | + /* Every reference must be matched with its definition. */ | |
1368 | + for (j = 0; j < list->nitems; ++j) { | |
1369 | + message_ty *crtmsg, *merge_msg; | |
1370 | + | |
1371 | + /* check for empty message ids == headers */ | |
1372 | + if (list->item[j]->msgid[0] == '\0') { | |
1373 | + continue; | |
1374 | + } | |
1375 | + | |
1376 | + if ((list->item[j])->is_fuzzy && (fuzzy == 0)) { | |
1377 | + /* we don't want fuzzies */ | |
1378 | + continue; | |
1379 | + } | |
1380 | + | |
1381 | + crtmsg = message_copy (list->item[j]); | |
1382 | + | |
1383 | + /* First, search its pair in the result list ... */ | |
1384 | + merge_msg = message_list_search (result, crtmsg->msgid); | |
1385 | + | |
1386 | + /* Now here is something broken: we get only the msgstr | |
1387 | + * from the variant[0] and override its domain with our master_id | |
1388 | + */ | |
1389 | + crtmsg->variant_count = 1; | |
1390 | + crtmsg->variant[0].domain = master_id; | |
1391 | + | |
1392 | + if (merge_msg != NULL) { | |
1393 | + int variants; | |
1394 | + void *temp; | |
1395 | + | |
1396 | + /* Ah! we have to do a nasty merge */ | |
1397 | + variants = ++merge_msg->variant_count; | |
1398 | + temp = realloc (merge_msg->variant, variants * sizeof(crtmsg->variant[0])); | |
1399 | + if (temp != NULL) { | |
1400 | + merge_msg->variant = temp; | |
1401 | + } else { | |
1402 | + error (EXIT_FAILURE, 0, "OUT of memory in %s", __FUNCTION__); | |
1403 | + exit(-1); | |
1404 | + } | |
1405 | + merge_msg->variant[variants-1] = crtmsg->variant[0]; | |
1406 | + /* that should be it */ | |
1407 | + } else { | |
1408 | + /* this is really simple - just add it */ | |
1409 | + message_list_append (result, crtmsg); | |
1410 | + } | |
1411 | + | |
1412 | + /* Remember that this message has been used, when we scan | |
1413 | + later to see if anything was omitted. */ | |
1414 | + crtmsg->used = 1; | |
1415 | + } | |
1416 | + | |
1417 | + return result ; | |
1418 | +} | |
1419 | + | |
1420 | +static message_list_ty * | |
1421 | +invert (fn) | |
1422 | + const char *fn; /* file name */ | |
1423 | +{ | |
1424 | + message_list_ty *list; | |
1425 | + size_t j; | |
1426 | + size_t inverted = 0; | |
1427 | + message_list_ty *result; | |
1428 | + | |
1429 | + /* Read in the file */ | |
1430 | + list = grammar (fn); | |
1431 | + | |
1432 | + result = message_list_alloc (); | |
1433 | + | |
1434 | + /* Every reference must be matched with its definition. */ | |
1435 | + for (j = 0; j < list->nitems; ++j) { | |
1436 | + message_ty *crtmsg; | |
1437 | + int k; | |
1438 | + | |
1439 | + /* check for empty message ids == headers */ | |
1440 | + if (list->item[j]->msgid[0] == '\0') { | |
1441 | + message_list_append (result, list->item[j]); | |
1442 | + continue; | |
1443 | + } | |
1444 | + | |
1445 | + crtmsg = message_copy (list->item[j]); | |
1446 | + | |
1447 | + /* | |
1448 | + * Now invert it. For each domain found in the file we create a record | |
1449 | + * containing the inverted pair | |
1450 | + */ | |
1451 | + for (k = 0; k < crtmsg->variant_count; k++) { | |
1452 | + message_ty *mp; | |
1453 | + const char *tmp; | |
1454 | + | |
1455 | + mp = message_copy(crtmsg); | |
1456 | + mp->variant_count = 1; | |
1457 | + tmp = mp->msgid; | |
1458 | + mp->msgid = crtmsg->variant[k].msgstr; | |
1459 | + mp->variant[0] = crtmsg->variant[k]; | |
1460 | + mp->variant[0].msgstr = tmp; | |
1461 | + message_list_append (result, mp); | |
1462 | + ++inverted; | |
1463 | + } | |
1464 | + | |
1465 | + /* Remember that this message has been used, when we scan | |
1466 | + later to see if anything was omitted. */ | |
1467 | + crtmsg->used = 1; | |
1468 | + } | |
1469 | + | |
1470 | + /* get rid of the duplicate message ids */ | |
1471 | + return un_duplicate(result); | |
1472 | +} | |
1473 | + | |
1474 | +static message_list_ty * | |
1475 | +missing (fn) | |
1476 | + const char *fn; /* file name */ | |
1477 | +{ | |
1478 | + message_list_ty *list; | |
1479 | + size_t j; | |
1480 | + message_list_ty *result; | |
1481 | + | |
1482 | + /* Read in the file */ | |
1483 | + list = grammar (fn); | |
1484 | + | |
1485 | + result = message_list_alloc (); | |
1486 | + | |
1487 | + /* Every reference must be matched with its definition. */ | |
1488 | + for (j = 0; j < list->nitems; ++j) { | |
1489 | + int k; | |
1490 | + | |
1491 | + /* check for empty message ids == headers */ | |
1492 | + if (list->item[j]->msgid[0] == '\0') { | |
1493 | + message_list_append (result, list->item[j]); | |
1494 | + continue; | |
1495 | + } | |
1496 | + | |
1497 | + /* Fuzzies go in */ | |
1498 | + if (list->item[j]->is_fuzzy) { | |
1499 | + message_list_append (result, list->item[j]); | |
1500 | + continue; | |
1501 | + } | |
1502 | + | |
1503 | + /* Check for empty translations */ | |
1504 | + for (k = 0; k < list->item[j]->variant_count; k++) { | |
1505 | + if (strlen(list->item[j]->variant[k].msgstr) <= 1) { | |
1506 | + message_list_append (result, list->item[j]); | |
1507 | + break; | |
1508 | + } | |
1509 | + } | |
1510 | + | |
1511 | + /* Remember that this message has been used, when we scan | |
1512 | + later to see if anything was omitted. */ | |
1513 | + list->item[j]->used = 1; | |
1514 | + } | |
1515 | + return result; | |
1516 | +} | |
1517 | + | |
1518 | +static message_list_ty * | |
1519 | +un_duplicate (dup_list) | |
1520 | + message_list_ty *dup_list; /* list containing duplicates */ | |
1521 | +{ | |
1522 | + message_list_ty *result; | |
1523 | + size_t j; | |
1524 | + | |
1525 | + result = message_list_alloc (); | |
1526 | + | |
1527 | + /* First iteration, clear the used flag */ | |
1528 | + for (j = 0; j < dup_list->nitems; ++j) | |
1529 | + dup_list->item[j]->used = 0; | |
1530 | + | |
1531 | + /* This is a cheesy O(n^2) dupe elimination "algorithm" | |
1532 | + * One day I will replace it by a quicksort() followed by | |
1533 | + * a walk through the whole list one more time and that will bring | |
1534 | + * it down to O(n log(n)) */ | |
1535 | + for (j = 0; j < dup_list->nitems; ++j) { | |
1536 | + message_ty *crtmsg; | |
1537 | + size_t k; | |
1538 | + | |
1539 | + /* get the current message */ | |
1540 | + crtmsg = dup_list->item[j]; | |
1541 | + | |
1542 | + if (crtmsg->used > 0) | |
1543 | + continue; | |
1544 | + | |
1545 | + for (k = j + 1; k < dup_list->nitems; ++k) { | |
1546 | + message_ty *newmsg; | |
1547 | + size_t i; | |
1548 | + | |
1549 | + newmsg = dup_list->item[k]; | |
1550 | + | |
1551 | + if (strcmp(crtmsg->msgid, newmsg->msgid) != 0 || | |
1552 | + newmsg->used > 0) | |
1553 | + continue; | |
1554 | + | |
1555 | + if (newmsg->comment) | |
1556 | + for (i = 0; i < newmsg->comment->nitems; ++i) | |
1557 | + message_comment_append (crtmsg, newmsg->comment->item[i]); | |
1558 | + | |
1559 | + if (newmsg->comment_dot) | |
1560 | + for (i = 0; i < newmsg->comment_dot->nitems; ++i) | |
1561 | + message_comment_dot_append (crtmsg, newmsg->comment_dot->item[i]); | |
1562 | + | |
1563 | + /* file position comments */ | |
1564 | + for (i = 0; i < newmsg->filepos_count; ++i) { | |
1565 | + lex_pos_ty *pp = &newmsg->filepos[i]; | |
1566 | + message_comment_filepos (crtmsg, pp->file_name, pp->line_number); | |
1567 | + } | |
1568 | + | |
1569 | + fprintf(stderr, "%05d : %05d\r", j, k); | |
1570 | + /* Now mark it as used */ | |
1571 | + newmsg->used++; | |
1572 | + } | |
1573 | + | |
1574 | + /* Now put the resulting entry on the final list */ | |
1575 | + message_list_append (result, crtmsg); | |
1576 | + } | |
1577 | + | |
1578 | + return result; | |
1579 | + | |
1580 | +} | |
1581 | + | |
1582 | +message_ty * | |
1583 | +message_join (def, ref) | |
1584 | + message_ty *def; | |
1585 | + message_ty *ref; | |
1586 | +{ | |
1587 | + message_ty *result; | |
1588 | + size_t j; | |
1589 | + char *meta_comment; | |
1590 | + | |
1591 | + /* is this the header ? */ | |
1592 | + if (ref->msgid[0] == '\0') { | |
1593 | + result = message_copy(ref); | |
1594 | + return result; | |
1595 | + }; | |
1596 | + | |
1597 | + | |
1598 | + /* XXX: FIX ME!!! | |
1599 | + * We are not processing in any way the variant list at this time. | |
1600 | + * We assume blindly that joining two pairs onn variant[0] will yield | |
1601 | + * the correct result | |
1602 | + */ | |
1603 | + | |
1604 | + /* here it goes: (m,X) join m(m,N) = (N,X) */ | |
1605 | + result = message_copy(def); | |
1606 | + /* here we should be looping over all domains */ | |
1607 | + result->msgid = ref->variant[0].msgstr; | |
1608 | + | |
c155f01f | 1609 | + if (!useless) { |
1610 | + /* Trim down on crap translations */ | |
1611 | + if (result->variant[0].msgstr == NULL) | |
1612 | + return NULL; | |
1613 | + if (strlen(result->variant[0].msgstr) == 0) | |
1614 | + return NULL; | |
1615 | + if (strcmp(result->msgid, result->variant[0].msgstr) == 0) | |
1616 | + return NULL; | |
1617 | + } | |
1618 | + | |
fb4f7b36 | 1619 | + /* Take the comments from the definition file. There will be none at |
1620 | + * all in the reference file, as it was generated by xgettext. | |
1621 | + */ | |
1622 | + /* we don't do that here because result is already initialized from def | |
1623 | + --cristiang */ | |
1624 | + /*if (def->comment) */ | |
1625 | + /* for (j = 0; j < def->comment->nitems; ++j) */ | |
1626 | + /* message_comment_append (result, def->comment->item[j]); */ | |
1627 | + | |
1628 | + /* now add the meta information for the common join field */ | |
1629 | + j = strlen(ref->msgid) + 10; | |
1630 | + meta_comment = alloca(j); | |
1631 | + if (meta_comment != NULL) { | |
1632 | + snprintf(meta_comment, j-1, "meta=%s", ref->msgid); /* should be safe */ | |
1633 | + message_comment_append(result, meta_comment); | |
1634 | + } else { | |
1635 | + message_comment_append(result, "meta=ERROR!!!"); | |
1636 | + message_comment_append(result, ref->msgid); | |
1637 | + } | |
1638 | + | |
1639 | + /* Take the dot comments from the reference file, as they are | |
1640 | + generated by xgettext. Any in the definition file are old ones | |
1641 | + collected by previous runs of xgettext and msgmerge. */ | |
1642 | + if (ref->comment_dot) | |
1643 | + for (j = 0; j < ref->comment_dot->nitems; ++j) | |
1644 | + message_comment_dot_append (result, ref->comment_dot->item[j]); | |
1645 | + | |
1646 | +/* message_comment_dot_append(result, "this is my test"); */ | |
1647 | + /* The flags are mixed in a special way. Some informations come | |
1648 | + from the reference message (such as format/no-format), others | |
1649 | + come from the definition file (fuzzy or not). */ | |
1650 | + result->is_fuzzy = def->is_fuzzy; | |
1651 | + result->is_c_format = ref->is_c_format; | |
1652 | + result->do_wrap = ref->do_wrap; | |
1653 | + | |
1654 | + /* Take the file position comments from the reference file, as they | |
1655 | + are generated by xgettext. Any in the definition file are old ones | |
1656 | + collected by previous runs of xgettext and msgmerge. */ | |
1657 | + for (j = 0; j < ref->filepos_count; ++j) { | |
1658 | + lex_pos_ty *pp = &ref->filepos[j]; | |
1659 | + message_comment_filepos (result, pp->file_name, pp->line_number); | |
1660 | + } | |
1661 | + | |
1662 | + /* All done, return the merged message to the caller. */ | |
1663 | + return result; | |
1664 | +} | |
1665 | + | |
1666 | +static message_list_ty * | |
1667 | +split (fn, flag) | |
1668 | + const char *fn; /* file name */ | |
1669 | + int flag; /* what should we be splitting out */ | |
1670 | +{ | |
1671 | + message_list_ty *list; | |
1672 | + size_t j; | |
1673 | + message_list_ty *result; | |
1674 | + | |
1675 | + /* Read in the file */ | |
1676 | + list = grammar (fn); | |
1677 | + | |
1678 | + result = message_list_alloc (); | |
1679 | + | |
1680 | + /* Every reference must be matched with its definition. */ | |
1681 | + for (j = 0; j < list->nitems; ++j) { | |
1682 | + message_ty *crtmsg; | |
1683 | + | |
1684 | + /* get the current message */ | |
1685 | + crtmsg = message_copy (list->item[j]); | |
1686 | + | |
1687 | + /* search all comments looking for our meta= */ | |
1688 | + if (crtmsg->comment) { | |
1689 | + int k; | |
1690 | + for (k = 0; k < crtmsg->comment->nitems; ++k) { | |
1691 | + char *tmp; | |
1692 | + | |
1693 | + tmp = strstr(crtmsg->comment->item[k], "meta="); | |
1694 | + if (tmp != NULL) { /* found it */ | |
1695 | + message_ty *res; | |
1696 | + int i; | |
1697 | + message_variant_ty *mvp = NULL; | |
1698 | + | |
1699 | + res = message_alloc(xstrdup(tmp+5)); | |
1700 | + res->variant_count = 1; | |
1701 | + mvp = malloc(sizeof(struct message_variant_ty)); | |
1702 | + if (mvp == NULL) { | |
1703 | + error (EXIT_FAILURE, 0, _("Weird out of memory situation")); | |
1704 | + usage (EXIT_FAILURE); | |
1705 | + } | |
1706 | + *mvp = crtmsg->variant[0]; | |
1707 | + res->variant = mvp; | |
1708 | + switch(flag) { | |
1709 | + case SPLIT_MSGID: | |
1710 | + res->variant[0].msgstr = crtmsg->msgid; | |
1711 | + /* transport over all the dot comments */ | |
1712 | + if (crtmsg->comment_dot) | |
1713 | + for (i = 0; i < crtmsg->comment_dot->nitems; ++i) | |
1714 | + message_comment_dot_append(res, crtmsg->comment_dot->item[i]); | |
1715 | + break; | |
1716 | + case SPLIT_MSGSTR: | |
1717 | + res->variant[0].msgstr = crtmsg->variant[0].msgstr; | |
1718 | + /* transport over the comments with the exception of | |
1719 | + the dot comments and the meta= one */ | |
1720 | + for (i = 0; i < crtmsg->comment->nitems; ++i) | |
1721 | + if (i != k) | |
1722 | + message_comment_append(res, crtmsg->comment->item[i]); | |
1723 | + break; | |
1724 | + default: | |
1725 | + error (EXIT_SUCCESS, 0, _("Unknwon split mode")); | |
1726 | + usage (EXIT_FAILURE); | |
1727 | + } | |
1728 | + message_list_append (result, res); | |
1729 | + } | |
1730 | + } | |
1731 | + } else { | |
1732 | + /* we have no meta comment on this one */ | |
1733 | + continue; | |
1734 | + } | |
1735 | + } | |
1736 | + return result; | |
1737 | +} | |
1738 | + | |
1739 | +static message_list_ty * | |
1740 | +explode (fn, flag) | |
1741 | + const char *fn; /* file name */ | |
1742 | + int flag; /* what should we be splitting out */ | |
1743 | +{ | |
1744 | + message_list_ty *list; | |
1745 | + size_t j; | |
1746 | + message_list_ty *result; | |
1747 | + | |
1748 | + /* Read in the file */ | |
1749 | + list = grammar (fn); | |
1750 | + | |
1751 | + result = message_list_alloc (); | |
1752 | + | |
1753 | + /* Every reference ... */ | |
1754 | + for (j = 0; j < list->nitems; ++j) { | |
1755 | + message_ty *crtmsg; | |
1756 | + int k; | |
1757 | + | |
1758 | + /* Get the current message */ | |
1759 | + crtmsg = message_copy (list->item[j]); | |
1760 | + | |
1761 | + /* Every "conformant" cross reference ... */ | |
1762 | + for (k = 0; k < crtmsg->filepos_count; ++k) { | |
1763 | + lex_pos_ty *pp; | |
1764 | + char buf[1024]; | |
1765 | + const char *fn, *tn; | |
1766 | + const char *f, *fe; | |
1767 | + int ln; | |
1768 | + message_ty *res; | |
1769 | + int i; | |
1770 | + message_variant_ty *mvp = NULL; | |
1771 | + | |
1772 | + pp = &crtmsg->filepos[k]; | |
1773 | + fn = pp->file_name; | |
1774 | + ln = pp->line_number; | |
1775 | + | |
1776 | + switch(ln) { | |
1777 | + default: | |
1778 | + tn = NULL; | |
1779 | + break; | |
1780 | + case 1004: | |
1781 | + tn = "(Summary)"; | |
1782 | + break; | |
1783 | + case 1005: | |
1784 | + tn = "(Description)"; | |
1785 | + break; | |
1786 | + case 1016: | |
1787 | + tn = "(Group)"; | |
1788 | + break; | |
1789 | + } | |
1790 | + | |
1791 | + /* ... weird tags we do not want */ | |
1792 | + if (tn == NULL) | |
1793 | + continue; | |
1794 | + | |
1795 | + /* Find the text after the name-version-release */ | |
1796 | + if ((f = strrchr(fn, '-')) == NULL || | |
1797 | + (fe = strchr(f, '.')) == NULL) { | |
1798 | + fprintf(stderr, _("skipping malformed xref \"%s\"\n"), fn); | |
1799 | + continue; | |
1800 | + } | |
1801 | + fe = fn + strlen(fn) - sizeof(".src.rpm") + 1; | |
1802 | + | |
1803 | + /* ... src.rpm's we do not want */ | |
1804 | + if ( !strcmp(fe, ".src.rpm") ) | |
1805 | + continue; | |
1806 | + | |
1807 | + /* Position at end of name */ | |
1808 | + fe = strrchr(fn, '-') - 1; | |
1809 | + while (fe > fn && *fe != '-') | |
1810 | + --fe; | |
1811 | + | |
1812 | + /* Construct meta tag */ | |
1813 | + strncpy(buf, fn, (fe-fn) ); | |
1814 | + buf[fe-fn] = '\0'; | |
1815 | + strncat(buf, tn, (sizeof(buf) - strlen(buf)) ); | |
1816 | + | |
1817 | + res = message_alloc(xstrdup(buf)); | |
1818 | + res->variant_count = 1; | |
1819 | + mvp = malloc(sizeof(struct message_variant_ty)); | |
1820 | + if (mvp == NULL) { | |
1821 | + error (EXIT_FAILURE, 0, _("Weird out of memory situation")); | |
1822 | + usage (EXIT_FAILURE); | |
1823 | + } | |
1824 | + *mvp = crtmsg->variant[0]; | |
1825 | + res->variant = mvp; | |
1826 | + switch(flag) { | |
1827 | + case EXPLODE_MSGID: | |
1828 | + res->variant[0].msgstr = crtmsg->msgid; | |
1829 | + /* transport over all the dot comments */ | |
1830 | + if (crtmsg->comment_dot) | |
1831 | + for (i = 0; i < crtmsg->comment_dot->nitems; ++i) | |
1832 | + message_comment_dot_append(res, crtmsg->comment_dot->item[i]); | |
1833 | + break; | |
1834 | + case EXPLODE_MSGSTR: | |
1835 | + res->variant[0].msgstr = crtmsg->variant[0].msgstr; | |
1836 | +#if 0 | |
1837 | + /* transport over the comments with the exception of | |
1838 | + the dot comments */ | |
1839 | + for (i = 0; i < crtmsg->comment->nitems; ++i) | |
1840 | + message_comment_append(res, crtmsg->comment->item[i]); | |
1841 | +#endif | |
1842 | + break; | |
1843 | + default: | |
1844 | + error (EXIT_SUCCESS, 0, _("Unknwon explode mode")); | |
1845 | + usage (EXIT_FAILURE); | |
1846 | + } | |
1847 | + message_list_append (result, res); | |
1848 | + } | |
1849 | + } | |
1850 | + return result; | |
1851 | +} | |
1852 | + | |
fb4f7b36 | 1853 | +static message_list_ty * |
1854 | +empty (fn) | |
1855 | + const char *fn; /* file name */ | |
1856 | +{ | |
1857 | + message_list_ty *list; | |
1858 | + size_t j; | |
1859 | + int had_header = 0; | |
1860 | + message_list_ty *result; | |
1861 | + | |
1862 | + /* Read in the file */ | |
1863 | + list = grammar (fn); | |
1864 | + | |
1865 | + result = message_list_alloc (); | |
1866 | + | |
1867 | + /* Every reference must be matched with its definition. */ | |
1868 | + for (j = 0; j < list->nitems; ++j) { | |
1869 | + if (list->item[j]->msgid[0] == '\0') { | |
1870 | + had_header++; | |
1871 | + break; | |
1872 | + } | |
1873 | + } | |
1874 | + | |
1875 | + /* If we don't have a header, we should try to add one */ | |
c155f01f | 1876 | + if (!had_header) |
1877 | + message_list_append (result, new_header()); | |
1878 | + | |
1879 | + /* time to add the rest */ | |
1880 | + for (j = 0; j < list->nitems; ++j) { | |
1881 | + message_ty *crtmsg; | |
1882 | + | |
1883 | + crtmsg = list->item[j]; | |
1884 | + if (crtmsg->msgid[0] != '\0') { | |
1885 | + /* empty the translations */ | |
1886 | + crtmsg->variant_count = 1; | |
1887 | + crtmsg->variant[0].msgstr = ""; | |
1888 | + } | |
1889 | + crtmsg->used = 1; | |
1890 | + message_list_append (result, crtmsg); | |
1891 | + } | |
1892 | + return result; | |
1893 | +} | |
1894 | + | |
1895 | +static message_list_ty * | |
1896 | +dummy (fn) | |
1897 | + const char *fn; /* file name */ | |
1898 | +{ | |
1899 | + message_list_ty *list; | |
1900 | + size_t j; | |
1901 | + int had_header = 0; | |
1902 | + message_list_ty *result; | |
1903 | + | |
1904 | + /* Read in the file */ | |
1905 | + list = grammar (fn); | |
1906 | + | |
1907 | + result = message_list_alloc (); | |
1908 | + | |
1909 | + /* Every reference must be matched with its definition. */ | |
1910 | + for (j = 0; j < list->nitems; ++j) { | |
1911 | + if (list->item[j]->msgid[0] == '\0') { | |
1912 | + had_header++; | |
1913 | + break; | |
1914 | + } | |
1915 | + } | |
1916 | + | |
1917 | + /* If we don't have a header, we should try to add one */ | |
1918 | + if (!had_header) | |
1919 | + message_list_append (result, new_header()); | |
1920 | + | |
1921 | + /* time to add the rest */ | |
1922 | + for (j = 0; j < list->nitems; ++j) { | |
1923 | + message_ty *crtmsg; | |
1924 | + | |
1925 | + crtmsg = list->item[j]; | |
1926 | + if (crtmsg->msgid[0] != '\0') { | |
1927 | + /* dummy translation */ | |
1928 | + crtmsg->variant_count = 1; | |
1929 | + crtmsg->variant[0].msgstr = crtmsg->msgid; | |
1930 | + } | |
1931 | + crtmsg->used = 1; | |
1932 | + message_list_append (result, crtmsg); | |
1933 | + } | |
1934 | + return result; | |
1935 | +} | |
1936 | + | |
1937 | +#define TM_YEAR_ORIGIN 1900 | |
1938 | +static struct message_ty * | |
1939 | +new_header(void) | |
1940 | +{ | |
fb4f7b36 | 1941 | + time_t now; |
1942 | + struct tm local_time; | |
1943 | + message_ty *mp; | |
1944 | + char *msgstr; | |
1945 | + static lex_pos_ty pos = { __FILE__, __LINE__, }; | |
1946 | + | |
1947 | + mp = message_alloc (""); | |
c155f01f | 1948 | + if (mp == NULL) |
1949 | + return NULL; | |
1950 | + | |
fb4f7b36 | 1951 | + message_comment_append (mp, "\ |
1952 | +SOME DESCRIPTIVE TITLE.\n\ | |
1953 | +FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n"); | |
1954 | + | |
1955 | + time (&now); | |
1956 | + local_time = *localtime (&now); | |
1957 | + | |
1958 | + asprintf (&msgstr, "\ | |
1959 | +Project-Id-Version: PACKAGE VERSION\n\ | |
1960 | +POT-Creation-Date: %d-%02d-%02d %02d:%02d\n\ | |
1961 | +PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n\ | |
1962 | +Last-Translator: FULL NAME <EMAIL@ADDRESS>\n\ | |
1963 | +Language-Team: LANGUAGE <LL@li.org>\n\ | |
1964 | +MIME-Version: 1.0\n\ | |
1965 | +Content-Type: text/plain; charset=CHARSET\n\ | |
1966 | +Content-Transfer-Encoding: ENCODING\n", | |
1967 | + local_time.tm_year + TM_YEAR_ORIGIN, | |
1968 | + local_time.tm_mon + 1, | |
1969 | + local_time.tm_mday, | |
1970 | + local_time.tm_hour, | |
1971 | + local_time.tm_min); | |
1972 | + | |
1973 | + if (msgstr == NULL) | |
1974 | + error(EXIT_FAILURE, errno, _("while preparing output")); | |
1975 | + message_variant_append (mp, MESSAGE_DOMAIN_DEFAULT, msgstr, &pos); | |
c155f01f | 1976 | + return mp; |
fb4f7b36 | 1977 | +} |