poparser.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. #include <ctype.h>
  2. #include <assert.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include "poparser.h"
  6. #include "StringEscape.h"
  7. #define streq(A, B) (!strcmp(A, B))
  8. #define strstarts(S, W) (memcmp(S, W, sizeof(W) - 1) ? NULL : (S + (sizeof(W) - 1)))
  9. static enum po_entry get_type_and_start(char* lp, char* end, size_t *stringstart) {
  10. enum po_entry result_type;
  11. char *x, *y;
  12. size_t start = (size_t) lp;
  13. while(isspace(*lp) && lp < end) lp++;
  14. if(lp[0] == '#') {
  15. inv:
  16. *stringstart = 0;
  17. return pe_invalid;
  18. }
  19. if((y = strstarts(lp, "msg"))) {
  20. if((x = strstarts(y, "id")) && (isspace(*x) || ((x = strstarts(x, "_plural")) && isspace(*x))))
  21. result_type = pe_msgid;
  22. else if ((x = strstarts(y, "str")) && (isspace(*x) ||
  23. (x[0] == '[' && (x[1] == '0' || x[1] == '1') && x[2] == ']' && (x += 3) && isspace(*x))))
  24. result_type = pe_msgstr;
  25. else
  26. goto inv;
  27. while(isspace(*x) && x < end) x++;
  28. if(*x != '"') abort();
  29. conv:
  30. *stringstart = ((size_t) x - start) + 1;
  31. } else if(*lp == '"') {
  32. result_type = pe_str;
  33. x = lp;
  34. goto conv;
  35. } else {
  36. goto inv;
  37. }
  38. return result_type;
  39. }
  40. /* expects a pointer to the first char after a opening " in a string,
  41. * converts the string into convbuf, and returns the length of that string */
  42. static size_t get_length_and_convert(char* x, char* end, char* convbuf, size_t convbuflen) {
  43. size_t result = 0;
  44. char* e = x + strlen(x);
  45. assert(e > x && e < end && *e == 0);
  46. e--;
  47. while(isspace(*e)) e--;
  48. if(*e != '"') abort();
  49. *e = 0;
  50. result = unescape(x, convbuf, convbuflen);
  51. return result;
  52. }
  53. void poparser_init(struct po_parser *p, char* workbuf, size_t bufsize, poparser_callback cb, void* cbdata) {
  54. p->buf = workbuf;
  55. p->bufsize = bufsize;
  56. p->cb = cb;
  57. p->prev_type = pe_invalid;
  58. p->curr_len = 0;
  59. p->cbdata = cbdata;
  60. }
  61. enum lineactions {
  62. la_incr,
  63. la_proc,
  64. la_abort,
  65. la_nop,
  66. la_max,
  67. };
  68. /* return 0 on success */
  69. int poparser_feed_line(struct po_parser *p, char* line, size_t buflen) {
  70. char *convbuf = p->buf;
  71. size_t convbuflen = p->bufsize;
  72. size_t strstart;
  73. static const enum lineactions action_tbl[pe_max][pe_max] = {
  74. // pe_str will never be set as curr_type
  75. [pe_str] = {
  76. [pe_str] = la_abort,
  77. [pe_msgid] = la_abort,
  78. [pe_msgstr] = la_abort,
  79. [pe_invalid] = la_abort,
  80. },
  81. [pe_msgid] = {
  82. [pe_str] = la_incr,
  83. [pe_msgid] = la_proc,
  84. [pe_msgstr] = la_proc,
  85. [pe_invalid] = la_proc,
  86. },
  87. [pe_msgstr] = {
  88. [pe_str] = la_incr,
  89. [pe_msgid] = la_proc,
  90. [pe_msgstr] = la_proc,
  91. [pe_invalid] = la_proc,
  92. },
  93. [pe_invalid] = {
  94. [pe_str] = la_nop, // this can happen when we have msgstr[2] "" ... "foo", since we only parse msgstr[0] and [1]
  95. [pe_msgid] = la_incr,
  96. [pe_msgstr] = la_incr,
  97. [pe_invalid] = la_nop,
  98. },
  99. };
  100. enum po_entry type;
  101. type = get_type_and_start(line, line + buflen, &strstart);
  102. switch(action_tbl[p->prev_type][type]) {
  103. case la_incr:
  104. assert(type == pe_msgid || type == pe_msgstr || type == pe_str);
  105. p->curr_len += get_length_and_convert(line + strstart, line + buflen, convbuf + p->curr_len, convbuflen - p->curr_len);
  106. break;
  107. case la_proc:
  108. assert(p->prev_type == pe_msgid || p->prev_type == pe_msgstr);
  109. p->info.text = convbuf;
  110. p->info.textlen = p->curr_len;
  111. p->info.type = p->prev_type;
  112. p->cb(&p->info, p->cbdata);
  113. if(type != pe_invalid)
  114. p->curr_len = get_length_and_convert(line + strstart, line + buflen, convbuf, convbuflen);
  115. else
  116. p->curr_len = 0;
  117. break;
  118. case la_nop:
  119. break;
  120. case la_abort:
  121. default:
  122. abort();
  123. // todo : return error code
  124. }
  125. if(type != pe_str) {
  126. p->prev_type = type;
  127. }
  128. return 0;
  129. }
  130. int poparser_finish(struct po_parser *p) {
  131. char empty[4] = "";
  132. return poparser_feed_line(p, empty, sizeof(empty));
  133. }