1 /* msgfmt utility (C) 2012 rofl0r
2 * released under the MIT license, see LICENSE for details */
9 #include "StringEscape.h"
11 __attribute__((noreturn
))
12 static void syntax(void) {
14 "Usage: msgmerge [OPTION] def.po ref.pot\n");
18 __attribute__((noreturn
))
19 static void version(void) {
21 "these are not (GNU gettext-tools) 99.9999.9999\n");
25 #define streq(A, B) (!strcmp(A, B))
26 #define strstarts(S, W) (memcmp(S, W, sizeof(W) - 1) ? NULL : (S + (sizeof(W) - 1)))
30 /* we can haz 3 different input files:
31 * the .pot, which is the file containing only the ripped out strings from the program
32 * (and no translations)
33 * a .po, which contains translations and strings made from a previous .pot from that same source file,
34 * a compendium, which is basically a huge po file containing all sorts of strings (msgid's) and translations (msgstr's)
41 enum po_entry prev_type
;
44 /* currently we only output input strings as output strings
45 * i.e. there is no translation lookup at all */
46 int process_line_callback(struct po_info
* info
, void* user
) {
47 struct fiLes
* file
= (struct fiLes
*) user
;
49 // escape what is unescaped automatically by lib
50 escape(info
->text
, file
->convbuf
, sizeof(file
->convbuf
));
53 file
->plural_count
= 1;
54 fprintf(file
->out
, "\nmsgid \"%s\"\n", file
->convbuf
);
55 file
->prev_type
= info
->type
;
58 fprintf(file
->out
, "msgctxt \"%s\"\n", file
->convbuf
);
61 fprintf(file
->out
, "msgid_plural \"%s\"\n", file
->convbuf
);
62 file
->prev_type
= info
->type
;
65 if (file
->prev_type
== pe_plural
) {
66 fprintf(file
->out
, "msgstr[%d] \"%s\"\n", file
->plural_count
++, file
->convbuf
);
68 fprintf(file
->out
, "msgstr \"%s\"\n", file
->convbuf
);
75 int process(struct fiLes
*files
, int update
, int backup
) {
76 (void) update
; (void) backup
;
77 struct po_parser pb
, *p
= &pb
;
78 char line
[4096], conv
[8192], *lb
;
79 poparser_init(p
, conv
, sizeof(conv
), process_line_callback
, files
);
80 while((lb
= fgets(line
, sizeof(line
), files
->po
))) {
81 poparser_feed_line(p
, lb
, sizeof(line
));
87 void set_file(int out
, char* fn
, FILE** dest
) {
89 *dest
= out
? stdout
: stdin
;
91 *dest
= fopen(fn
, out
? "w" : "r");
99 int getbackuptype(char* str
) {
100 if(!str
|| !*str
|| streq(str
, "none") || streq(str
, "off"))
102 else if(streq(str
, "t") || streq(str
, "numbered"))
104 else if(streq(str
, "nil") || streq(str
, "existing"))
106 else if(streq(str
, "simple") || streq(str
, "never"))
111 int main(int argc
, char**argv
) {
112 if(argc
== 1) syntax();
125 struct fiLes files
= {0,0,0,0,1,0};
126 char* backup_suffix
= getenv("SIMPLE_BACKUP_SUFFIX");
127 if(!backup_suffix
) backup_suffix
= "~";
129 int backup
= getbackuptype(getenv("VERSION_CONTROL"));
131 set_file(1, "-", &files
.out
);
133 for(; arg
< argc
; arg
++) {
137 streq(A
+2, "strict") ||
138 streq(A
+2, "properties-input") ||
139 streq(A
+2, "properties-output") ||
140 streq(A
+2, "stringtable-input") ||
141 streq(A
+2, "stringtable-output") ||
142 streq(A
+2, "no-fuzzy-matching") ||
143 streq(A
+2, "multi-domain") ||
144 streq(A
+2, "previous") ||
145 streq(A
+2, "escape") ||
146 streq(A
+2, "no-escape") ||
147 streq(A
+2, "force-po") ||
148 streq(A
+2, "indent") ||
149 streq(A
+2, "add-location") ||
150 streq(A
+2, "no-location") ||
151 streq(A
+2, "no-wrap") ||
152 streq(A
+2, "sort-output") ||
153 streq(A
+2, "sort-by-file") ||
155 strstarts(A
+2, "lang=") ||
156 strstarts(A
+2, "color") || // can be --color or --color=xxx
157 strstarts(A
+2, "style=") ||
158 strstarts(A
+2, "width=") ||
160 streq(A
+2, "verbose") ||
161 streq(A
+2, "quiet") ||
162 streq(A
+2, "silent") ) {
163 } else if(streq(A
+2, "version")) {
165 } else if((dest
= strstarts(A
+2, "output-file="))) {
166 set_file(1, dest
, &files
.out
);
167 } else if((dest
= strstarts(A
+2, "compendium="))) {
168 set_file(1, dest
, &files
.compend
);
169 } else if((dest
= strstarts(A
+2, "suffix="))) {
170 backup_suffix
= dest
;
171 } else if((dest
= strstarts(A
+2, "directory="))) {
173 } else if((dest
= strstarts(A
+2, "backup"))) {
175 backup
= getbackuptype(dest
+ 1);
178 } else if(streq(A
+2, "update")) {
181 } else if(streq(A
+2, "help")) syntax();
183 } else if(streq(A
+ 1, "o")) {
185 } else if(streq(A
+ 1, "C")) {
186 expect_fn
.compend
= 1;
187 } else if(streq(A
+ 1, "U")) {
204 } else if (streq(A
+1, "v")) {
206 } else if (streq(A
+1, "D")) {
207 // no support for -D at this time
209 fprintf(stderr
, "EINVAL\n");
211 } else if (streq(A
+1, "h")) {
213 } else if(expect_fn
.out
) {
214 if(update
&& streq(A
, "/dev/null")) return 0;
215 set_file(1, A
, &files
.out
);
217 } else if(expect_fn
.compend
) {
218 set_file(1, A
, &files
.compend
);
219 expect_fn
.compend
= 0;
220 } else if(expect_fn
.po
) {
221 if(update
&& streq(A
, "/dev/null")) return 0;
222 set_file(0, A
, &files
.po
);
225 } else if(expect_fn
.pot
) {
226 if(update
&& streq(A
, "/dev/null")) return 0;
227 set_file(0, A
, &files
.pot
);
231 } else if(expect_fn
.out
) {
232 if(update
&& streq(A
, "/dev/null")) return 0;
233 set_file(1, A
, &files
.out
);
235 } else if(expect_fn
.compend
) {
236 set_file(1, A
, &files
.compend
);
237 expect_fn
.compend
= 0;
238 } else if(expect_fn
.po
) {
239 if(update
&& streq(A
, "/dev/null")) return 0;
240 set_file(0, A
, &files
.po
);
243 } else if(expect_fn
.pot
) {
244 if(update
&& streq(A
, "/dev/null")) return 0;
245 set_file(0, A
, &files
.pot
);
250 fprintf(stdout
, "warning: update functionality unimplemented\n");
253 if(!files
.out
|| !files
.po
|| !files
.pot
) syntax();
254 int ret
= process(&files
, update
, backup
);
255 FILE** filearr
= (FILE**) &files
;
257 for (i
= 0; i
< 4; i
++) {
258 if(filearr
[i
] != NULL
) fflush(filearr
[i
]);
260 for (i
= 0; i
< 4; i
++) {
262 filearr
[i
] != NULL
&&
263 filearr
[i
] != stdout
&&
265 ) fclose(filearr
[i
]);