msgfmt.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  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. // in DO_NOTHING mode, we simply write the msgid twice, once for msgid, once for msgstr.
  10. // TODO: maybe make it write "" instead of echoing the msgid.
  11. //#define DO_NOTHING
  12. __attribute__((noreturn))
  13. static void syntax(void) {
  14. fprintf(stdout,
  15. "Usage: msgfmt [OPTION] filename.po ...\n");
  16. exit(1);
  17. }
  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);
  23. }
  24. #define streq(A, B) (!strcmp(A, B))
  25. #define strstarts(S, W) (memcmp(S, W, sizeof(W) - 1) ? NULL : (S + (sizeof(W) - 1)))
  26. struct mo_hdr {
  27. unsigned magic;
  28. int rev;
  29. unsigned numstring;
  30. unsigned off_tbl_org;
  31. unsigned off_tbl_trans;
  32. unsigned hash_tbl_size;
  33. unsigned off_tbl_hash;
  34. };
  35. /* file layout:
  36. header
  37. strtable (lenghts/offsets)
  38. transtable (lenghts/offsets)
  39. [hashtable]
  40. strings section
  41. translations section */
  42. const struct mo_hdr def_hdr = {
  43. 0x950412de,
  44. 0,
  45. 0,
  46. sizeof(struct mo_hdr),
  47. 0,
  48. 0,
  49. 0,
  50. };
  51. // pass 0: collect numbers of strings, calculate size and offsets for tables
  52. // print header
  53. // pass 1: create in-memory string tables
  54. enum passes {
  55. pass_first = 0,
  56. pass_collect_sizes = pass_first,
  57. pass_second,
  58. pass_max,
  59. };
  60. struct strtbl {
  61. unsigned len, off;
  62. };
  63. struct strmap {
  64. struct strtbl str, *trans;
  65. };
  66. struct callbackdata {
  67. enum passes pass;
  68. unsigned off;
  69. FILE* out;
  70. unsigned msgidbuf1_len;
  71. unsigned msgidbuf2_len;
  72. unsigned pluralbuf1_len;
  73. unsigned pluralbuf2_len;
  74. unsigned ctxtbuf_len;
  75. unsigned msgstr1_len;
  76. unsigned msgstr2_len;
  77. unsigned pluralstr_count;
  78. unsigned string_maxlen;
  79. char* msgidbuf1;
  80. char* msgidbuf2;
  81. char* pluralbuf1;
  82. char* pluralbuf2;
  83. char* msgctxtbuf;
  84. char* msgstrbuf1;
  85. char* msgstrbuf2;
  86. unsigned priv_type;
  87. unsigned priv_len;
  88. unsigned num[pe_maxstr];
  89. unsigned len[pe_maxstr];
  90. struct strmap *strlist;
  91. struct strtbl *translist;
  92. char *strbuffer[pe_maxstr];
  93. unsigned stroff[pe_maxstr];
  94. unsigned curr[pe_maxstr];
  95. };
  96. static struct callbackdata *cb_for_qsort;
  97. int strmap_comp(const void *a_, const void *b_) {
  98. const struct strmap *a = a_, *b = b_;
  99. return strcmp(cb_for_qsort->strbuffer[0] + a->str.off, cb_for_qsort->strbuffer[0] + b->str.off);
  100. }
  101. enum sysdep_types {
  102. st_priu32 = 0,
  103. st_priu64,
  104. st_priumax,
  105. st_max
  106. };
  107. static const char sysdep_str[][10]={
  108. [st_priu32] = "\x08<PRIu32>",
  109. [st_priu64] = "\x08<PRIu64>",
  110. [st_priumax] = "\x09<PRIuMAX>",
  111. };
  112. static const char sysdep_repl[][8]={
  113. [st_priu32] = "\x02lu\0u",
  114. [st_priu64] = "\x02lu\0llu",
  115. [st_priumax] = "\x01ju"
  116. };
  117. static const char *get_repl(enum sysdep_types type, unsigned nr) {
  118. assert(nr < (unsigned)sysdep_repl[type][0]);
  119. const char* p = sysdep_repl[type]+1;
  120. while(nr--) p+=strlen(p)+1;
  121. return p;
  122. }
  123. static void replace(char* text, unsigned textlen, const char* what, const char * with) {
  124. char*p = text;
  125. size_t la = strlen(what), li=strlen(with);
  126. assert(la >= li);
  127. for(p=text;textlen >= la;) {
  128. if(!memcmp(p,what,la)) {
  129. memcpy(p, with, li);
  130. textlen -= la;
  131. memmove(p+li,p+la,textlen+1);
  132. p+=li;
  133. } else {
  134. p++;
  135. textlen--;
  136. }
  137. }
  138. }
  139. static unsigned get_form(enum sysdep_types type, unsigned no, unsigned occurences[st_max]) {
  140. unsigned i,divisor = 1;
  141. for(i=type+1;i<st_max;i++) if(occurences[i]) divisor *= sysdep_repl[i][0];
  142. return (no/divisor)%sysdep_repl[type][0];
  143. }
  144. static char** sysdep_transform(const char* text, unsigned textlen, unsigned *len, unsigned *count, int simulate) {
  145. unsigned occurences[st_max] = {0};
  146. const char *p=text,*o;
  147. unsigned i,j, l = textlen;
  148. while(l && (o=strchr(p, '<'))) {
  149. l-=o-p;p=o;
  150. unsigned f = 0;
  151. for(i=0;i<st_max;i++)
  152. if(l>=(unsigned)sysdep_str[i][0] && !memcmp(p,sysdep_str[i]+1,sysdep_str[i][0])) {
  153. occurences[i]++;
  154. f=1;
  155. p+=sysdep_str[i][0];
  156. l-=sysdep_str[i][0];
  157. break;
  158. }
  159. if(!f) p++,l--;
  160. }
  161. *count = 1;
  162. for(i=0;i<st_max;i++) if(occurences[i]) *count *= sysdep_repl[i][0];
  163. l = textlen * *count;
  164. for(i=0;i<*count;i++) for(j=0;j<st_max;j++)
  165. if(occurences[j]) l-= occurences[j] * (sysdep_str[j][0] - strlen(get_repl(j, get_form(j, i, occurences))));
  166. *len = l+*count-1;
  167. char **out = 0;
  168. if(!simulate) {
  169. out = malloc((sizeof(char*)+textlen+1) * *count);
  170. assert(out);
  171. char *p = (void*)(out+*count);
  172. for(i=0;i<*count;i++) {
  173. out[i]=p;
  174. memcpy(p, text, textlen+1);
  175. p+=textlen+1;
  176. }
  177. for(i=0;i<*count;i++) for(j=0;j<st_max;j++)
  178. if(occurences[j])
  179. replace(out[i], textlen, sysdep_str[j]+1, get_repl(j, get_form(j, i, occurences)));
  180. }
  181. return out;
  182. }
  183. static inline void writemsg(struct callbackdata *d) {
  184. if(d->msgidbuf1_len != 0) {
  185. if(!d->strlist[d->curr[pe_msgid]].str.off)
  186. d->strlist[d->curr[pe_msgid]].str.off=d->stroff[pe_msgid];
  187. if(d->ctxtbuf_len != 0) {
  188. memcpy(d->strbuffer[pe_msgid] + d->stroff[pe_msgid], d->msgctxtbuf, d->ctxtbuf_len);
  189. d->strlist[d->curr[pe_msgid]].str.len+=d->ctxtbuf_len;
  190. d->stroff[pe_msgid]+=d->ctxtbuf_len;
  191. }
  192. memcpy(d->strbuffer[pe_msgid] + d->stroff[pe_msgid], d->msgidbuf1, d->msgidbuf1_len);
  193. d->stroff[pe_msgid]+=d->msgidbuf1_len;
  194. d->strlist[d->curr[pe_msgid]].str.len+=d->msgidbuf1_len-1;
  195. if(d->pluralbuf1_len != 0) {
  196. memcpy(d->strbuffer[pe_msgid] + d->stroff[pe_msgid], d->pluralbuf1, d->pluralbuf1_len);
  197. d->strlist[d->curr[pe_msgid]].str.len+=d->pluralbuf1_len;
  198. d->stroff[pe_msgid]+=d->pluralbuf1_len;
  199. }
  200. d->curr[pe_msgid]++;
  201. }
  202. if(d->msgidbuf2_len != 0) {
  203. if(!d->strlist[d->curr[pe_msgid]].str.off)
  204. d->strlist[d->curr[pe_msgid]].str.off=d->stroff[pe_msgid];
  205. if(d->ctxtbuf_len != 0) {
  206. memcpy(d->strbuffer[pe_msgid] + d->stroff[pe_msgid], d->msgctxtbuf, d->ctxtbuf_len);
  207. d->strlist[d->curr[pe_msgid]].str.len+=d->ctxtbuf_len;
  208. d->stroff[pe_msgid]+=d->ctxtbuf_len;
  209. }
  210. memcpy(d->strbuffer[pe_msgid] + d->stroff[pe_msgid], d->msgidbuf2, d->msgidbuf2_len);
  211. d->stroff[pe_msgid]+=d->msgidbuf2_len;
  212. d->strlist[d->curr[pe_msgid]].str.len+=d->msgidbuf2_len-1;
  213. if(d->pluralbuf2_len != 0) {
  214. memcpy(d->strbuffer[pe_msgid] + d->stroff[pe_msgid], d->pluralbuf2, d->pluralbuf2_len);
  215. d->strlist[d->curr[pe_msgid]].str.len+=d->pluralbuf2_len;
  216. d->stroff[pe_msgid]+=d->pluralbuf2_len;
  217. }
  218. d->curr[pe_msgid]++;
  219. }
  220. d->pluralbuf2_len=d->pluralbuf1_len=d->ctxtbuf_len=d->msgidbuf1_len=d->msgidbuf2_len=0;
  221. }
  222. static inline void writestr(struct callbackdata *d, struct po_info *info) {
  223. // msgid xx; msgstr ""; is widely happened, it's invalid
  224. // https://github.com/sabotage-linux/gettext-tiny/issues/1
  225. // no invalid, when empty, check d->num[pe_msgid]
  226. if(!d->pluralstr_count && d->num[pe_msgid] > 0) {
  227. d->len[pe_msgid]-=d->msgidbuf1_len;
  228. d->len[pe_msgid]-=d->msgidbuf2_len;
  229. d->len[pe_plural]-=d->pluralbuf1_len;
  230. d->len[pe_plural]-=d->pluralbuf2_len;
  231. d->len[pe_ctxt]-=d->ctxtbuf_len;
  232. d->len[pe_msgstr]--;
  233. d->num[pe_msgid]--;
  234. d->num[pe_msgstr]--;
  235. d->pluralbuf2_len=d->pluralbuf1_len=d->ctxtbuf_len=d->msgidbuf1_len=d->msgidbuf2_len=d->msgstr1_len=d->msgstr2_len=d->pluralstr_count=0;
  236. return;
  237. }
  238. if(d->pluralstr_count && d->pluralstr_count <= info->nplurals) {
  239. writemsg(d);
  240. // plural <= nplurals is allowed
  241. d->translist[d->curr[pe_msgstr]].len=d->msgstr1_len-1;
  242. d->translist[d->curr[pe_msgstr]].off=d->stroff[pe_msgstr];
  243. d->strlist[d->curr[pe_msgstr]].trans = &d->translist[d->curr[pe_msgstr]];
  244. memcpy(d->strbuffer[pe_msgstr] + d->stroff[pe_msgstr], d->msgstrbuf1, d->msgstr1_len);
  245. d->stroff[pe_msgstr]+=d->msgstr1_len;
  246. d->curr[pe_msgstr]++;
  247. if(d->msgstr2_len) {
  248. d->translist[d->curr[pe_msgstr]].len=d->msgstr2_len-1;
  249. d->translist[d->curr[pe_msgstr]].off=d->stroff[pe_msgstr];
  250. d->strlist[d->curr[pe_msgstr]].trans = &d->translist[d->curr[pe_msgstr]];
  251. memcpy(d->strbuffer[pe_msgstr] + d->stroff[pe_msgstr], d->msgstrbuf2, d->msgstr2_len);
  252. d->stroff[pe_msgstr]+=d->msgstr2_len;
  253. d->curr[pe_msgstr]++;
  254. }
  255. d->msgstr1_len=d->msgstr2_len=d->pluralstr_count=0;
  256. }
  257. }
  258. int process_line_callback(struct po_info* info, void* user) {
  259. struct callbackdata *d = (struct callbackdata *) user;
  260. assert(info->type == pe_msgid || info->type == pe_ctxt || info->type == pe_msgstr || info->type == pe_plural);
  261. char **sysdeps;
  262. unsigned len, count, i, l;
  263. switch(d->pass) {
  264. case pass_collect_sizes:
  265. sysdep_transform(info->text, info->textlen, &len, &count, 1);
  266. d->num[info->type] += count;
  267. if(info->type == pe_msgid && count == 2 && d->priv_type == pe_ctxt) {
  268. // ctxt meets msgid with sysdeps, multiply num and len to suit it
  269. d->len[pe_ctxt] += d->priv_len +1;
  270. d->num[pe_ctxt]++;
  271. }
  272. if(count != 1 && info->type == pe_ctxt) {
  273. // except msgid, str, plural, all other types should not have sysdeps
  274. abort();
  275. }
  276. d->priv_type = info->type;
  277. d->priv_len = len;
  278. d->len[info->type] += len +1;
  279. if(len+1 > d->string_maxlen)
  280. d->string_maxlen = len+1;
  281. break;
  282. case pass_second:
  283. sysdeps = sysdep_transform(info->text, info->textlen, &len, &count, 0);
  284. for(i=0;i<count;i++) {
  285. l = strlen(sysdeps[i]);
  286. assert(l+1 <= d->string_maxlen);
  287. if(info->type == pe_msgid) {
  288. if(i==0 && d->msgidbuf1_len)
  289. writestr(d, info);
  290. // just copy, it's written down when writemsg()
  291. if(i==0) {
  292. memcpy(d->msgidbuf1, sysdeps[i], l+1);
  293. d->msgidbuf1_len = l+1;
  294. } else {
  295. memcpy(d->msgidbuf2, sysdeps[i], l+1);
  296. d->msgidbuf2_len = l+1;
  297. }
  298. } else if(info->type == pe_plural) {
  299. if(i==0) {
  300. memcpy(d->pluralbuf1, sysdeps[i], l+1);
  301. d->pluralbuf1_len = l+1;
  302. } else {
  303. memcpy(d->pluralbuf2, sysdeps[i], l+1);
  304. d->pluralbuf2_len = l+1;
  305. }
  306. } else if(info->type == pe_ctxt) {
  307. writestr(d, info);
  308. d->ctxtbuf_len = l+1;
  309. memcpy(d->msgctxtbuf, sysdeps[i], l);
  310. d->msgctxtbuf[l] = 0x4;//EOT
  311. } else {
  312. // just copy, it's written down when writestr()
  313. if(l) {
  314. if(i==0) {
  315. memcpy(&d->msgstrbuf1[d->msgstr1_len], sysdeps[i], l+1);
  316. d->msgstr1_len += l+1;
  317. d->pluralstr_count++;
  318. } else {
  319. // sysdeps exist
  320. memcpy(&d->msgstrbuf2[d->msgstr2_len], sysdeps[i], l+1);
  321. d->msgstr2_len += l+1;
  322. }
  323. }
  324. }
  325. }
  326. free(sysdeps);
  327. break;
  328. default:
  329. abort();
  330. }
  331. return 0;
  332. }
  333. int process(FILE *in, FILE *out) {
  334. struct mo_hdr mohdr = def_hdr;
  335. char line[4096]; char *lp;
  336. char convbuf[16384];
  337. struct callbackdata d = {
  338. .num = {
  339. [pe_msgid] = 0,
  340. [pe_msgstr] = 0,
  341. [pe_plural] = 0,
  342. [pe_ctxt] = 0,
  343. },
  344. .len = {
  345. [pe_msgid] = 0,
  346. [pe_msgstr] = 0,
  347. [pe_plural] = 0,
  348. [pe_ctxt] = 0,
  349. },
  350. .off = 0,
  351. .out = out,
  352. .pass = pass_first,
  353. .ctxtbuf_len = 0,
  354. .pluralbuf1_len = 0,
  355. .pluralbuf2_len = 0,
  356. .msgidbuf1_len = 0,
  357. .msgidbuf2_len = 0,
  358. .msgstr1_len = 0,
  359. .msgstr2_len = 0,
  360. .pluralstr_count = 0,
  361. .string_maxlen = 0,
  362. };
  363. struct po_parser pb, *p = &pb;
  364. mohdr.off_tbl_trans = mohdr.off_tbl_org;
  365. for(d.pass = pass_first; d.pass <= pass_second; d.pass++) {
  366. if(d.pass == pass_second) {
  367. // start of second pass:
  368. // ensure we dont output when there's no strings at all
  369. if(d.num[pe_msgid] == 0) {
  370. return 1;
  371. }
  372. // check that data gathered in first pass is consistent
  373. if((d.num[pe_msgstr] < d.num[pe_msgid]) || (d.num[pe_msgstr] > (d.num[pe_msgid] + d.num[pe_plural] * (p->info.nplurals - 1)))) {
  374. // one should actually abort here,
  375. // but gnu gettext simply writes an empty .mo and returns success.
  376. //abort();
  377. fprintf(stderr, "warning: mismatch of msgid/msgstr count, writing empty .mo file\n");
  378. d.num[pe_msgid] = 0;
  379. return 0;
  380. }
  381. d.msgidbuf1 = calloc(d.string_maxlen*5+2*d.string_maxlen*p->info.nplurals, 1);
  382. d.msgidbuf2 = d.msgidbuf1 + d.string_maxlen;
  383. d.pluralbuf1 = d.msgidbuf2 + d.string_maxlen;
  384. d.pluralbuf2 = d.pluralbuf1 + d.string_maxlen;
  385. d.msgctxtbuf = d.pluralbuf2 + d.string_maxlen;
  386. d.msgstrbuf1 = d.msgctxtbuf + d.string_maxlen;
  387. d.msgstrbuf2 = d.msgstrbuf1 + d.string_maxlen*p->info.nplurals;
  388. d.strlist = calloc(d.num[pe_msgid] * sizeof(struct strmap), 1);
  389. d.translist = calloc(d.num[pe_msgstr] * sizeof(struct strtbl), 1);
  390. d.strbuffer[pe_msgid] = calloc(d.len[pe_msgid]+d.len[pe_plural]+d.len[pe_ctxt], 1);
  391. d.strbuffer[pe_msgstr] = calloc(d.len[pe_msgstr], 1);
  392. d.stroff[pe_msgid] = d.stroff[pe_msgstr] = 0;
  393. assert(d.msgidbuf1 && d.strlist && d.translist && d.strbuffer[pe_msgid] && d.strbuffer[pe_msgstr]);
  394. }
  395. poparser_init(p, convbuf, sizeof(convbuf), process_line_callback, &d);
  396. while((lp = fgets(line, sizeof(line), in))) {
  397. poparser_feed_line(p, lp, sizeof(line));
  398. }
  399. poparser_finish(p);
  400. if(d.pass == pass_second)
  401. writestr(&d, &p->info);
  402. if(d.pass == pass_second) {
  403. // calculate header fields from len and num arrays
  404. mohdr.numstring = d.num[pe_msgid];
  405. mohdr.off_tbl_org = sizeof(struct mo_hdr);
  406. mohdr.off_tbl_trans = mohdr.off_tbl_org + d.num[pe_msgid] * (sizeof(unsigned)*2);
  407. // set offset startvalue
  408. d.off = mohdr.off_tbl_trans + d.num[pe_msgid] * (sizeof(unsigned)*2);
  409. }
  410. fseek(in, 0, SEEK_SET);
  411. }
  412. cb_for_qsort = &d;
  413. qsort(d.strlist, d.num[pe_msgid], sizeof (struct strmap), strmap_comp);
  414. unsigned i;
  415. // print header
  416. fwrite(&mohdr, sizeof(mohdr), 1, out);
  417. for(i = 0; i < d.num[pe_msgid]; i++) {
  418. d.strlist[i].str.off += d.off;
  419. fwrite(&d.strlist[i].str, sizeof(struct strtbl), 1, d.out);
  420. }
  421. for(i = 0; i < d.num[pe_msgid]; i++) {
  422. d.strlist[i].trans->off += d.off + d.len[pe_msgid] + d.len[pe_plural] + d.len[pe_ctxt];
  423. fwrite(d.strlist[i].trans, sizeof(struct strtbl), 1, d.out);
  424. }
  425. fwrite(d.strbuffer[pe_msgid], d.len[pe_msgid]+d.len[pe_plural]+d.len[pe_ctxt], 1, d.out);
  426. fwrite(d.strbuffer[pe_msgstr], d.len[pe_msgstr], 1, d.out);
  427. return 0;
  428. }
  429. void set_file(int out, char* fn, FILE** dest) {
  430. if(streq(fn, "-")) {
  431. if(out) {
  432. *dest = stdout;
  433. } else {
  434. char b[4096];
  435. size_t n=0;
  436. FILE* tmpf = tmpfile();
  437. if(!tmpf)
  438. perror("tmpfile");
  439. while((n=fread(b, sizeof(*b), sizeof(b), stdin)) > 0)
  440. fwrite(b, sizeof(*b), n, tmpf);
  441. fseek(tmpf, 0, SEEK_SET);
  442. *dest = tmpf;
  443. }
  444. } else {
  445. *dest = fopen(fn, out ? "w" : "r");
  446. }
  447. if(!*dest) {
  448. perror("fopen");
  449. exit(1);
  450. }
  451. }
  452. int main(int argc, char**argv) {
  453. if(argc == 1) syntax();
  454. int arg = 1;
  455. FILE *out = NULL;
  456. FILE *in = NULL;
  457. int expect_in_fn = 1;
  458. char* locale = NULL;
  459. char* dest = NULL;
  460. #define A argv[arg]
  461. for(; arg < argc; arg++) {
  462. if(A[0] == '-') {
  463. if(A[1] == '-') {
  464. if(
  465. streq(A+2, "java") ||
  466. streq(A+2, "java2") ||
  467. streq(A+2, "csharp") ||
  468. streq(A+2, "csharp-resources") ||
  469. streq(A+2, "tcl") ||
  470. streq(A+2, "qt") ||
  471. streq(A+2, "strict") ||
  472. streq(A+2, "properties-input") ||
  473. streq(A+2, "stringtable-input") ||
  474. streq(A+2, "use-fuzzy") ||
  475. strstarts(A+2, "alignment=") ||
  476. streq(A+2, "check") ||
  477. streq(A+2, "check-format") ||
  478. streq(A+2, "check-header") ||
  479. streq(A+2, "check-domain") ||
  480. streq(A+2, "check-compatibility") ||
  481. streq(A+2, "check-accelerators") ||
  482. streq(A+2, "no-hash") ||
  483. streq(A+2, "verbose") ||
  484. streq(A+2, "statistics") ||
  485. strstarts(A+2, "check-accelerators=") ||
  486. strstarts(A+2, "resource=")
  487. ) {
  488. } else if((dest = strstarts(A+2, "locale="))) {
  489. locale = dest;
  490. } else if((dest = strstarts(A+2, "output-file="))) {
  491. set_file(1, dest, &out);
  492. } else if(streq(A+2, "version")) {
  493. version();
  494. } else if(streq(A+2, "help")) {
  495. syntax();
  496. } else if (expect_in_fn) {
  497. set_file(0, A, &in);
  498. expect_in_fn = 0;
  499. }
  500. } else if(streq(A + 1, "o")) {
  501. arg++;
  502. dest = A;
  503. set_file(1, A, &out);
  504. } else if(
  505. streq(A+1, "j") ||
  506. streq(A+1, "r") ||
  507. streq(A+1, "P") ||
  508. streq(A+1, "f") ||
  509. streq(A+1, "a") ||
  510. streq(A+1, "c") ||
  511. streq(A+1, "v") ||
  512. streq(A+1, "C")
  513. ) {
  514. } else if (streq(A+1, "V")) {
  515. version();
  516. } else if (streq(A+1, "h")) {
  517. syntax();
  518. } else if (streq(A+1, "l")) {
  519. arg++;
  520. locale = A;
  521. } else if (streq(A+1, "d")) {
  522. arg++;
  523. dest = A;
  524. } else if (expect_in_fn) {
  525. set_file(0, A, &in);
  526. expect_in_fn = 0;
  527. }
  528. } else if (expect_in_fn) {
  529. set_file(0, A, &in);
  530. expect_in_fn = 0;
  531. }
  532. }
  533. if (locale != NULL && dest != NULL) {
  534. int sz = snprintf(NULL, 0, "%s/%s.msg", dest, locale);
  535. char msg[sz+1];
  536. snprintf(msg, sizeof(msg), "%s/%s.msg", dest, locale);
  537. FILE *fp = fopen(msg, "w");
  538. if (fp) {
  539. fclose(fp);
  540. return 0;
  541. } else return 1;
  542. }
  543. if(out == NULL) {
  544. dest = "messages.mo";
  545. set_file(1, "messages.mo", &out);
  546. }
  547. if(in == NULL || out == NULL) {
  548. return 1;
  549. }
  550. int ret = process(in, out);
  551. fflush(in); fflush(out);
  552. if(in != stdin) fclose(in);
  553. if(out != stdout) fclose(out);
  554. if (ret == 1) {
  555. return remove(dest);
  556. }
  557. return ret;
  558. }