fcntl.h: Make F_DUPFD_CLOEXEC if _USE_XOPEN2K8
[uclibc-ng.git] / utils / msgmerge.c
blob2a5e041ff421f9cd03ac81a360b7fa193e7514cc
1 /* msgfmt utility (C) 2012 rofl0r
2 * released under the MIT license, see LICENSE for details */
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <ctype.h>
7 #include <assert.h>
8 #include "poparser.h"
9 #include "StringEscape.h"
11 __attribute__((noreturn))
12 static void syntax(void) {
13 fprintf(stdout,
14 "Usage: msgmerge [OPTION] def.po ref.pot\n");
15 exit(1);
18 __attribute__((noreturn))
19 static void version(void) {
20 fprintf(stdout,
21 "these are not (GNU gettext-tools) 99.9999.9999\n");
22 exit(0);
25 #define streq(A, B) (!strcmp(A, B))
26 #define strstarts(S, W) (memcmp(S, W, sizeof(W) - 1) ? NULL : (S + (sizeof(W) - 1)))
28 struct fiLes {
29 FILE *out;
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)
36 FILE *po;
37 FILE *pot;
38 FILE *compend;
39 int plural_count;
40 char convbuf[16384];
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));
51 switch (info->type) {
52 case pe_msgid:
53 file->plural_count = 1;
54 fprintf(file->out, "\nmsgid \"%s\"\n", file->convbuf);
55 file->prev_type = info->type;
56 break;
57 case pe_ctxt:
58 fprintf(file->out, "msgctxt \"%s\"\n", file->convbuf);
59 break;
60 case pe_plural:
61 fprintf(file->out, "msgid_plural \"%s\"\n", file->convbuf);
62 file->prev_type = info->type;
63 break;
64 case pe_msgstr:
65 if (file->prev_type == pe_plural) {
66 fprintf(file->out, "msgstr[%d] \"%s\"\n", file->plural_count++, file->convbuf);
67 } else {
68 fprintf(file->out, "msgstr \"%s\"\n", file->convbuf);
70 break;
72 return 0;
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));
83 poparser_finish(p);
84 return 0;
87 void set_file(int out, char* fn, FILE** dest) {
88 if(streq(fn, "-")) {
89 *dest = out ? stdout : stdin;
90 } else {
91 *dest = fopen(fn, out ? "w" : "r");
93 if(!*dest) {
94 perror("fopen");
95 exit(1);
99 int getbackuptype(char* str) {
100 if(!str || !*str || streq(str, "none") || streq(str, "off"))
101 return 0;
102 else if(streq(str, "t") || streq(str, "numbered"))
103 return 1;
104 else if(streq(str, "nil") || streq(str, "existing"))
105 return 2;
106 else if(streq(str, "simple") || streq(str, "never"))
107 return 3;
108 else syntax();
111 int main(int argc, char**argv) {
112 if(argc == 1) syntax();
113 int arg = 1;
114 struct expect {
115 int out;
116 int po;
117 int pot;
118 int compend;
119 } expect_fn = {
120 .out = 0,
121 .po = 1,
122 .pot = 0,
123 .compend = 0,
125 struct fiLes files = {0,0,0,0,1,0};
126 char* backup_suffix = getenv("SIMPLE_BACKUP_SUFFIX");
127 if(!backup_suffix) backup_suffix = "~";
128 int update = 0;
129 int backup = getbackuptype(getenv("VERSION_CONTROL"));
130 char* dest;
131 set_file(1, "-", &files.out);
132 #define A argv[arg]
133 for(; arg < argc; arg++) {
134 if(A[0] == '-') {
135 if(A[1] == '-') {
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")) {
164 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="))) {
172 goto nodir;
173 } else if((dest = strstarts(A+2, "backup"))) {
174 if (*dest == '=')
175 backup = getbackuptype(dest + 1);
176 else
177 backup = 0;
178 } else if(streq(A+2, "update")) {
179 set_update:
180 update = 1;
181 } else if(streq(A+2, "help")) syntax();
183 } else if(streq(A + 1, "o")) {
184 expect_fn.out = 1;
185 } else if(streq(A + 1, "C")) {
186 expect_fn.compend = 1;
187 } else if(streq(A + 1, "U")) {
188 goto set_update;
189 } else if(
190 streq(A+1, "m") ||
191 streq(A+1, "N") ||
192 streq(A+1, "P") ||
193 streq(A+1, "e") ||
194 streq(A+1, "E") ||
195 streq(A+1, "i") ||
196 streq(A+1, "p") ||
197 streq(A+1, "w") ||
198 streq(A+1, "s") ||
199 streq(A+1, "F") ||
200 streq(A+1, "V") ||
201 streq(A+1, "q")
204 } else if (streq(A+1, "v")) {
205 version();
206 } else if (streq(A+1, "D")) {
207 // no support for -D at this time
208 nodir:
209 fprintf(stderr, "EINVAL\n");
210 exit(1);
211 } else if (streq(A+1, "h")) {
212 syntax();
213 } else if(expect_fn.out) {
214 if(update && streq(A, "/dev/null")) return 0;
215 set_file(1, A, &files.out);
216 expect_fn.out = 0;
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);
223 expect_fn.po = 0;
224 expect_fn.pot = 1;
225 } else if(expect_fn.pot) {
226 if(update && streq(A, "/dev/null")) return 0;
227 set_file(0, A, &files.pot);
228 expect_fn.pot = 0;
231 } else if(expect_fn.out) {
232 if(update && streq(A, "/dev/null")) return 0;
233 set_file(1, A, &files.out);
234 expect_fn.out = 0;
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);
241 expect_fn.po = 0;
242 expect_fn.pot = 1;
243 } else if(expect_fn.pot) {
244 if(update && streq(A, "/dev/null")) return 0;
245 set_file(0, A, &files.pot);
246 expect_fn.pot = 0;
249 if(update) {
250 fprintf(stdout, "warning: update functionality unimplemented\n");
251 return 0;
253 if(!files.out || !files.po || !files.pot) syntax();
254 int ret = process(&files, update, backup);
255 FILE** filearr = (FILE**) &files;
256 unsigned i;
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 &&
264 filearr[i] != stdin
265 ) fclose(filearr[i]);
267 return ret;