msgmerge.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  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"
  10. __attribute__((noreturn))
  11. static void syntax(void) {
  12. fprintf(stdout,
  13. "Usage: msgmerge [OPTION] def.po ref.pot\n");
  14. exit(1);
  15. }
  16. __attribute__((noreturn))
  17. static void version(void) {
  18. fprintf(stdout,
  19. "these are not (GNU gettext-tools) 99.9999.9999\n");
  20. exit(0);
  21. }
  22. #define streq(A, B) (!strcmp(A, B))
  23. #define strstarts(S, W) (memcmp(S, W, sizeof(W) - 1) ? NULL : (S + (sizeof(W) - 1)))
  24. struct fiLes {
  25. FILE *out;
  26. /* we can haz 3 different input files:
  27. * the .pot, which is the file containing only the ripped out strings from the program
  28. * (and no translations)
  29. * a .po, which contains translations and strings made from a previous .pot from that same source file,
  30. * a compendium, which is basically a huge po file containing all sorts of strings (msgid's) and translations (msgstr's)
  31. */
  32. FILE *po;
  33. FILE *pot;
  34. FILE *compend;
  35. int plural_count;
  36. char convbuf[16384];
  37. enum po_entry prev_type;
  38. };
  39. /* currently we only output input strings as output strings
  40. * i.e. there is no translation lookup at all */
  41. int process_line_callback(struct po_info* info, void* user) {
  42. struct fiLes* file = (struct fiLes*) user;
  43. // escape what is unescaped automatically by lib
  44. escape(info->text, file->convbuf, sizeof(file->convbuf));
  45. switch (info->type) {
  46. case pe_msgid:
  47. file->plural_count = 1;
  48. fprintf(file->out, "\nmsgid \"%s\"\n", file->convbuf);
  49. file->prev_type = info->type;
  50. break;
  51. case pe_ctxt:
  52. fprintf(file->out, "msgctxt \"%s\"\n", file->convbuf);
  53. break;
  54. case pe_plural:
  55. fprintf(file->out, "msgid_plural \"%s\"\n", file->convbuf);
  56. file->prev_type = info->type;
  57. break;
  58. case pe_msgstr:
  59. if (file->prev_type == pe_plural) {
  60. fprintf(file->out, "msgstr[%d] \"%s\"\n", file->plural_count++, file->convbuf);
  61. } else {
  62. fprintf(file->out, "msgstr \"%s\"\n", file->convbuf);
  63. }
  64. break;
  65. }
  66. return 0;
  67. }
  68. int process(struct fiLes *files, int update, int backup) {
  69. (void) update; (void) backup;
  70. struct po_parser pb, *p = &pb;
  71. char line[4096], conv[8192], *lb;
  72. poparser_init(p, conv, sizeof(conv), process_line_callback, files);
  73. while((lb = fgets(line, sizeof(line), files->po))) {
  74. poparser_feed_line(p, lb, sizeof(line));
  75. }
  76. poparser_finish(p);
  77. return 0;
  78. }
  79. void set_file(int out, char* fn, FILE** dest) {
  80. if(streq(fn, "-")) {
  81. *dest = out ? stdout : stdin;
  82. } else {
  83. *dest = fopen(fn, out ? "w" : "r");
  84. }
  85. if(!*dest) {
  86. perror("fopen");
  87. exit(1);
  88. }
  89. }
  90. int getbackuptype(char* str) {
  91. if(!str || !*str || streq(str, "none") || streq(str, "off"))
  92. return 0;
  93. else if(streq(str, "t") || streq(str, "numbered"))
  94. return 1;
  95. else if(streq(str, "nil") || streq(str, "existing"))
  96. return 2;
  97. else if(streq(str, "simple") || streq(str, "never"))
  98. return 3;
  99. else syntax();
  100. }
  101. int main(int argc, char**argv) {
  102. if(argc == 1) syntax();
  103. int arg = 1;
  104. struct expect {
  105. int out;
  106. int po;
  107. int pot;
  108. int compend;
  109. } expect_fn = {
  110. .out = 0,
  111. .po = 1,
  112. .pot = 0,
  113. .compend = 0,
  114. };
  115. struct fiLes files = {0,0,0,0,1,0};
  116. char* backup_suffix = getenv("SIMPLE_BACKUP_SUFFIX");
  117. if(!backup_suffix) backup_suffix = "~";
  118. int update = 0;
  119. int backup = getbackuptype(getenv("VERSION_CONTROL"));
  120. char* dest;
  121. set_file(1, "-", &files.out);
  122. #define A argv[arg]
  123. for(; arg < argc; arg++) {
  124. if(A[0] == '-') {
  125. if(A[1] == '-') {
  126. if(
  127. streq(A+2, "strict") ||
  128. streq(A+2, "properties-input") ||
  129. streq(A+2, "properties-output") ||
  130. streq(A+2, "stringtable-input") ||
  131. streq(A+2, "stringtable-output") ||
  132. streq(A+2, "no-fuzzy-matching") ||
  133. streq(A+2, "multi-domain") ||
  134. streq(A+2, "previous") ||
  135. streq(A+2, "escape") ||
  136. streq(A+2, "no-escape") ||
  137. streq(A+2, "force-po") ||
  138. streq(A+2, "indent") ||
  139. streq(A+2, "add-location") ||
  140. streq(A+2, "no-location") ||
  141. streq(A+2, "no-wrap") ||
  142. streq(A+2, "sort-output") ||
  143. streq(A+2, "sort-by-file") ||
  144. strstarts(A+2, "lang=") ||
  145. strstarts(A+2, "color") || // can be --color or --color=xxx
  146. strstarts(A+2, "style=") ||
  147. strstarts(A+2, "width=") ||
  148. streq(A+2, "verbose") ||
  149. streq(A+2, "quiet") ||
  150. streq(A+2, "silent") ) {
  151. } else if(streq(A+2, "version")) {
  152. version();
  153. } else if((dest = strstarts(A+2, "output-file="))) {
  154. set_file(1, dest, &files.out);
  155. } else if((dest = strstarts(A+2, "compendium="))) {
  156. set_file(1, dest, &files.compend);
  157. } else if((dest = strstarts(A+2, "suffix="))) {
  158. backup_suffix = dest;
  159. } else if((dest = strstarts(A+2, "directory="))) {
  160. goto nodir;
  161. } else if((dest = strstarts(A+2, "backup"))) {
  162. if (*dest == '=')
  163. backup = getbackuptype(dest + 1);
  164. else
  165. backup = 0;
  166. } else if(streq(A+2, "update")) {
  167. set_update:
  168. update = 1;
  169. } else if(streq(A+2, "help")) syntax();
  170. } else if(streq(A + 1, "o")) {
  171. expect_fn.out = 1;
  172. } else if(streq(A + 1, "C")) {
  173. expect_fn.compend = 1;
  174. } else if(streq(A + 1, "U")) {
  175. goto set_update;
  176. } else if(
  177. streq(A+1, "m") ||
  178. streq(A+1, "N") ||
  179. streq(A+1, "P") ||
  180. streq(A+1, "e") ||
  181. streq(A+1, "E") ||
  182. streq(A+1, "i") ||
  183. streq(A+1, "p") ||
  184. streq(A+1, "w") ||
  185. streq(A+1, "s") ||
  186. streq(A+1, "F") ||
  187. streq(A+1, "V") ||
  188. streq(A+1, "q")
  189. ) {
  190. } else if (streq(A+1, "v")) {
  191. version();
  192. } else if (streq(A+1, "D")) {
  193. // no support for -D at this time
  194. nodir:
  195. fprintf(stderr, "EINVAL\n");
  196. exit(1);
  197. } else if (streq(A+1, "h")) {
  198. syntax();
  199. } else if(expect_fn.out) {
  200. if(update && streq(A, "/dev/null")) return 0;
  201. set_file(1, A, &files.out);
  202. expect_fn.out = 0;
  203. } else if(expect_fn.compend) {
  204. set_file(1, A, &files.compend);
  205. expect_fn.compend = 0;
  206. } else if(expect_fn.po) {
  207. if(update && streq(A, "/dev/null")) return 0;
  208. set_file(0, A, &files.po);
  209. expect_fn.po = 0;
  210. expect_fn.pot = 1;
  211. } else if(expect_fn.pot) {
  212. if(update && streq(A, "/dev/null")) return 0;
  213. set_file(0, A, &files.pot);
  214. expect_fn.pot = 0;
  215. }
  216. } else if(expect_fn.out) {
  217. if(update && streq(A, "/dev/null")) return 0;
  218. set_file(1, A, &files.out);
  219. expect_fn.out = 0;
  220. } else if(expect_fn.compend) {
  221. set_file(1, A, &files.compend);
  222. expect_fn.compend = 0;
  223. } else if(expect_fn.po) {
  224. if(update && streq(A, "/dev/null")) return 0;
  225. set_file(0, A, &files.po);
  226. expect_fn.po = 0;
  227. expect_fn.pot = 1;
  228. } else if(expect_fn.pot) {
  229. if(update && streq(A, "/dev/null")) return 0;
  230. set_file(0, A, &files.pot);
  231. expect_fn.pot = 0;
  232. }
  233. }
  234. if(update) {
  235. fprintf(stdout, "warning: update functionality unimplemented\n");
  236. return 0;
  237. }
  238. if(!files.out || !files.po || !files.pot) syntax();
  239. int ret = process(&files, update, backup);
  240. FILE** filearr = (FILE**) &files;
  241. unsigned i;
  242. for (i = 0; i < 4; i++) {
  243. if(filearr[i] != NULL) fflush(filearr[i]);
  244. }
  245. for (i = 0; i < 4; i++) {
  246. if(
  247. filearr[i] != NULL &&
  248. filearr[i] != stdout &&
  249. filearr[i] != stdin
  250. ) fclose(filearr[i]);
  251. }
  252. return ret;
  253. }