чување d1019afbfcd2a3efc2dcf18a03d1d285d28f0d72
родитељ 49f04f3225ed8a22d58ace022f10af1c2e83cb28
Аутор: Страхиња Радић <sr@strahinja.org>
Датум: Thu, 12 Feb 2026 18:56:09 +0100
po.c(load_file): Improve binary file and syntax check; (parse_po_line): Improve parsing
Diffstat:
| M | po.c | | | 270 | +++++++++++++++++++++++++++++++++++++++++++++++++------------------------------ |
| M | po.h | | | 41 | ++++++++++++++++++++--------------------- |
| M | poe.c | | | 13 | +++++++++++-- |
измењених датотека: 3, додавања: 200(+), брисања: 124(-)
diff --git a/po.c b/po.c
@@ -421,18 +421,19 @@ add_ref_comment(struct PoEntry* entry, const char* comment)
int
load_file(struct DrawState* dstate, long* lineno, long* col)
{
- struct PoEntry* newchunk = NULL;
- struct PoEntry* entry = NULL;
- struct PoEntry* current = NULL;
- FILE* input = NULL;
- char* input_line = NULL;
+ struct PoEntry* newchunk = NULL;
+ struct PoEntry* entry = NULL;
+ struct PoEntry* current = NULL;
+ FILE* input = NULL;
+ char* input_line = NULL;
ParseState pstate = PS_NONE;
int has_plural_msgid = 0;
- ssize_t input_size = limits[L_LINE_MAX];
+ ssize_t input_size = limits[L_LINE_MAX];
ssize_t non_obsolete_sofar = 0;
/* lines to tolerate the lack of msgid */
int msgid_counter = PO_DETECTION_LINES;
int msgstr_index = -1;
+ int possible_nulbyte;
assert(dstate != NULL);
input_line = malloc(input_size);
@@ -455,27 +456,44 @@ load_file(struct DrawState* dstate, long* lineno, long* col)
dstate->action = ACT_READING;
redraw_status(dstate);
+ possible_nulbyte = 0;
+ *lineno = 1;
while (!feof(input))
{
- char* eol = NULL;
- int result;
+ char* eol = NULL;
+ char* nulbyte = NULL;
if (!fgets(input_line, input_size, input))
continue;
+ if (possible_nulbyte)
+ {
+ /* Definitely not EOF, NUL byte confirmed */
+ dstate->error = ERR_NOTPO_FILE;
+ fclose(input);
+ free(input_line);
+ return 1;
+ }
eol = strchr(input_line, '\n');
if (eol)
*eol = 0;
+ nulbyte = strchr(input_line, 0);
- result = parse_po_line(input_line, dstate, &entry, &pstate, col,
+ /* We don't know if the NUL byte not being equal to
+ * (possibly NULL) eol is the result of EOF or not
+ */
+ if (nulbyte && (nulbyte != eol))
+ possible_nulbyte = 1;
+
+ parse_po_line(input_line, dstate, &entry, &pstate, col,
&msgstr_index);
if (entry && entry->msgid_plural)
has_plural_msgid = 1;
- switch(result)
+ switch (dstate->error)
{
- case PARSE_ERR_NONE:
+ case ERR_NONE:
if (pstate & PS_MSGID)
msgid_counter = -1;
else if (msgid_counter != -1)
@@ -487,13 +505,14 @@ load_file(struct DrawState* dstate, long* lineno, long* col)
return 1;
}
break;
- case PARSE_ERR_SYNTAX:
- dstate->error = ERR_SYNTAX;
- return 1;
+ case ERR_SYNTAX:
+ /* Allow for ERR_NOTPO_FILE to override ERR_SYNTAX */
+ break;
default:
goto load_file_alloc_error;
}
- (*lineno)++;
+ if (!dstate->error)
+ (*lineno)++;
}
/* when the .po file is not a standard text file (no LF at end) */
@@ -1069,49 +1088,52 @@ save_file_cleanup:
return result;
}
+#define INC_COL() \
+ do \
+ { \
+ if (!dstate->error) \
+ (*col)++; \
+ } while (0)
+
int
parse_po_line(const char* line, struct DrawState* dstate,
struct PoEntry** current_entry, ParseState* pstate, long* col,
int* msgstr_index)
{
- size_t i = 0;
- *col = 1;
const char* pline = line;
- char token[MAXBUFLINE];
- *token = 0;
- char* ptoken = token;
+ char* token = NULL;
+ char* ptoken = NULL;
+ size_t i = 0;
+ size_t len;
assert(line != NULL);
- /*if (!line)
- return PARSE_ERR_INVAL;*/
- if (starts_with(line, "msgid "))
- {
- size_t len = strlen("msgid ");
- *pstate |= PS_MSGID;
- pline += len;
- *col += len;
- }
- else if (starts_with(line, "msgid_plural "))
+ *col = 1;
+
+ token = malloc(limits[L_LINE_MAX]);
+ if (!token)
+ goto parse_po_line_alloc_error;
+ *token = 0;
+ ptoken = token;
+
+ if (starts_with(line, "msgid_plural"))
{
- size_t len = strlen("msgid_plural ");
+ len = strlen("msgid_plural");
*pstate &= ~PS_MSGID;
*pstate |= PS_MSGID_PLURAL;
pline += len;
*col += len;
}
- else if (starts_with(line, "msgstr "))
+ else if (starts_with(line, "msgid"))
{
- size_t len = strlen("msgstr ");
- *pstate &= ~PS_MSGID;
- *pstate &= ~PS_MSGID_PLURAL;
- *pstate |= PS_MSGSTR;
+ len = strlen("msgid");
+ *pstate |= PS_MSGID;
pline += len;
*col += len;
}
else if (starts_with(line, "msgstr["))
{
- size_t len = strlen("msgstr[");
+ len = strlen("msgstr[");
*pstate &= ~PS_MSGID;
*pstate &= ~PS_MSGID_PLURAL;
*pstate |= PS_MSGSTR_ARRAY;
@@ -1131,7 +1153,19 @@ parse_po_line(const char* line, struct DrawState* dstate,
(*col) += 2;
}
else
- return PARSE_ERR_SYNTAX;
+ {
+ dstate->error = ERR_SYNTAX;
+ goto parse_po_cleanup;
+ }
+ }
+ else if (starts_with(line, "msgstr"))
+ {
+ len = strlen("msgstr");
+ *pstate &= ~PS_MSGID;
+ *pstate &= ~PS_MSGID_PLURAL;
+ *pstate |= PS_MSGSTR;
+ pline += len;
+ *col += len;
}
else if (*line == 0)
{
@@ -1141,121 +1175,155 @@ parse_po_line(const char* line, struct DrawState* dstate,
*pstate &= ~PS_MSGSTR_ARRAY;
}
else if (*line != ' ' && *line != '\t' && *line != '"' && *line != '#')
- return PARSE_ERR_SYNTAX;
+ {
+ dstate->error = ERR_SYNTAX;
+ goto parse_po_cleanup;
+ }
while (*pline && i < strlen(line))
{
switch (*pline)
{
+ case ' ':
+ case '\t':
+ if (*pstate & PS_COMMENT)
+ {
+ *ptoken++ = *pline++;
+ INC_COL();
+ }
+ else if (*pstate & PS_STRING)
+ {
+ *ptoken++ = *pline++;
+ INC_COL();
+ }
+ else
+ {
+ pline++;
+ INC_COL();
+ }
+ break;
case '#':
- if (*col == 1)
+ if (pline - line + 1 == 1)
{
*pstate |= PS_COMMENT_TRAN;
pline++;
- (*col)++;
+ INC_COL();
}
- else
+ else if (*pstate & (PS_STRING | PS_COMMENT))
{
*ptoken++ = *pline++;
- (*col)++;
+ INC_COL();
+ }
+ else
+ {
+ dstate->error = ERR_SYNTAX;
+ goto parse_po_cleanup;
}
break;
case '.':
- if (*col == 2 && *pstate & PS_COMMENT_TRAN)
+ if (pline - line + 1 == 2 && *pstate & PS_COMMENT_TRAN)
{
*pstate &= ~PS_COMMENT_TRAN;
*pstate |= PS_COMMENT_EXTR;
pline++;
- (*col)++;
- if (*pline == ' ')
- {
- pline++;
- (*col)++;
- }
- else if (*pline)
- return PARSE_ERR_SYNTAX;
+ INC_COL();
}
else
{
*ptoken++ = *pline++;
- (*col)++;
+ INC_COL();
}
break;
case ',':
- if (*col == 2 && *pstate & PS_COMMENT_TRAN)
+ if (pline - line + 1 == 2 && *pstate & PS_COMMENT_TRAN)
{
*pstate &= ~PS_COMMENT_TRAN;
*pstate |= PS_COMMENT_FLAG;
pline++;
- (*col)++;
- if (*pline == ' ')
- {
- pline++;
- (*col)++;
- }
- else if (*pline)
- return PARSE_ERR_SYNTAX;
+ INC_COL();
}
else
{
*ptoken++ = *pline++;
- (*col)++;
+ INC_COL();
}
break;
case ':':
- if (*col == 2 && *pstate & PS_COMMENT_TRAN)
+ if (pline - line + 1 == 2 && *pstate & PS_COMMENT_TRAN)
{
*pstate &= ~PS_COMMENT_TRAN;
*pstate |= PS_COMMENT_REFN;
pline++;
- (*col)++;
- if (*pline == ' ')
- {
- pline++;
- (*col)++;
- }
- else if (*pline)
- return PARSE_ERR_SYNTAX;
+ INC_COL();
}
else
{
*ptoken++ = *pline++;
- (*col)++;
+ INC_COL();
}
break;
case '~':
- if (*col == 2 && *pstate & PS_COMMENT_TRAN)
+ if (pline - line + 1 == 2 && (*pstate & PS_COMMENT_TRAN))
{
*pstate &= ~PS_COMMENT_TRAN;
*pstate |= PS_COMMENT_OBSO;
pline++;
- (*col)++;
+ INC_COL();
}
else
{
*ptoken++ = *pline++;
- (*col)++;
+ INC_COL();
+ }
+ break;
+ case '\\':
+ if (*pstate & PS_COMMENT)
+ {
+ pline++;
+ INC_COL();
+ }
+ else if ((*pstate & PS_STRING) && *(pstate + 1))
+ {
+ *ptoken++ = *pline++;
+ *ptoken++ = *pline++;
+ INC_COL();
}
break;
case '"':
- /* skip beginning and end quote */
- if ((ptoken == token || *(pline + 1) == 0)
- && !(*pstate
- & (PS_COMMENT_TRAN | PS_COMMENT_EXTR
- | PS_COMMENT_OBSO)))
+ if (*pstate & PS_COMMENT)
+ {
+ pline++;
+ INC_COL();
+ }
+ else if (*pstate & PS_STRING)
{
+ *pstate &= ~PS_STRING;
pline++;
- (*col)++;
+ INC_COL();
}
else
{
- *ptoken++ = *pline++;
- (*col)++;
+ *pstate |= PS_STRING;
+ pline++;
+ INC_COL();
}
break;
default:
- *ptoken++ = *pline++;
- (*col)++;
+ if (*pstate & PS_COMMENT)
+ {
+ *ptoken++ = *pline++;
+ INC_COL();
+ }
+ else if (*pstate & PS_STRING)
+ {
+ *ptoken++ = *pline++;
+ INC_COL();
+ }
+ else
+ {
+ dstate->error = ERR_SYNTAX;
+ goto parse_po_cleanup;
+ }
}
}
*ptoken = 0;
@@ -1273,7 +1341,7 @@ parse_po_line(const char* line, struct DrawState* dstate,
newchunk = realloc(dstate->entries,
dstate->msgid_size * sizeof(struct PoEntry));
if (!newchunk)
- return PARSE_ERR_ALLOC;
+ goto parse_po_line_alloc_error;
dstate->entries = newchunk;
*current_entry = dstate->entries + dstate->msgid_size
- ALLOC_DELTA;
@@ -1292,7 +1360,7 @@ parse_po_line(const char* line, struct DrawState* dstate,
for (int i = 0; i < dstate->nplurals; i++)
if (!set_msgstr(*current_entry, NULL, i,
0, dstate))
- return PARSE_ERR_ALLOC;
+ goto parse_po_line_alloc_error;
}
else if (*pstate & PS_MSGSTR)
{
@@ -1309,13 +1377,13 @@ parse_po_line(const char* line, struct DrawState* dstate,
}
if (!set_msgstr(*current_entry, token, 0, 1, dstate))
- return PARSE_ERR_ALLOC;
+ goto parse_po_line_alloc_error;
}
else if (*pstate & PS_MSGSTR_ARRAY)
{
if (!set_msgstr(*current_entry, token, *msgstr_index, 1,
dstate))
- return PARSE_ERR_ALLOC;
+ goto parse_po_line_alloc_error;
}
else if (*pstate & PS_COMMENT_TRAN)
{
@@ -1323,7 +1391,7 @@ parse_po_line(const char* line, struct DrawState* dstate,
size_t len = strlen(token) + 1;
u32_token = calloc(len, sizeof(uint32_t));
if (!u32_token)
- return PARSE_ERR_ALLOC;
+ goto parse_po_line_alloc_error;
u8_string_to_unicode(u32_token, token, len);
u32_add_comment(*current_entry, u32_token);
free(u32_token);
@@ -1334,7 +1402,7 @@ parse_po_line(const char* line, struct DrawState* dstate,
size_t len = strlen(token) + 1;
u32_token = calloc(len, sizeof(uint32_t));
if (!u32_token)
- return PARSE_ERR_ALLOC;
+ goto parse_po_line_alloc_error;
u8_string_to_unicode(u32_token, token, len);
u32_add_trans_comment(*current_entry, u32_token);
free(u32_token);
@@ -1374,11 +1442,7 @@ parse_po_line(const char* line, struct DrawState* dstate,
{
/* Let this slip, ignore everything
* instead; see po.h for rationale */
- /*return PARSE_ERR_SYNTAX;*/
-
- while (*ptoken && *ptoken != ' '
- && *ptoken != ','
- && *ptoken != '\t')
+ while (*ptoken)
ptoken++;
}
}
@@ -1391,7 +1455,7 @@ parse_po_line(const char* line, struct DrawState* dstate,
size_t len = strlen(token) + 1;
u32_token = calloc(len, sizeof(uint32_t));
if (!u32_token)
- return PARSE_ERR_ALLOC;
+ goto parse_po_line_alloc_error;
u8_string_to_unicode(u32_token, token, len);
if (u32_add_comment(*current_entry, u32_token))
(*current_entry)->obsolete = 1;
@@ -1405,10 +1469,14 @@ parse_po_line(const char* line, struct DrawState* dstate,
u32_add_trans_comment(*current_entry, (uint32_t*)L"");
/* one-line states */
- *pstate &= ~(PS_COMMENT_TRAN | PS_COMMENT_EXTR | PS_COMMENT_FLAG
- | PS_COMMENT_REFN | PS_COMMENT_OBSO);
+ *pstate &= ~PS_COMMENT;
- return PARSE_ERR_NONE;
+parse_po_cleanup:
+ return !!dstate->error;
+
+parse_po_line_alloc_error:
+ dstate->error = ERR_CANT_ALLOC;
+ return 1;
}
void
diff --git a/po.h b/po.h
@@ -11,11 +11,11 @@
/* If msgid/msgstr is longer, it will be saved as multiple lines */
#define SAVE_MULTILINE_TRESHOLD 70
-#define LOAD_PARSE_ERR(e) (LOAD_ERR_PARSE_PREFIX + (e))
+// #define LOAD_PARSE_ERR(e) (LOAD_ERR_PARSE_PREFIX + (e))
struct DrawState;
-enum {
+/*enum {
PARSE_ERR_NONE = 0,
PARSE_ERR_INVAL = 1,
PARSE_ERR_ALLOC = 2,
@@ -31,7 +31,7 @@ enum {
LOAD_ERR_P_INVAL = LOAD_PARSE_ERR(PARSE_ERR_INVAL),
LOAD_ERR_P_ALLOC = LOAD_PARSE_ERR(PARSE_ERR_ALLOC),
LOAD_ERR_P_SYNTAX = LOAD_PARSE_ERR(PARSE_ERR_SYNTAX)
-};
+};*/
enum {
SAVE_ERR_NONE = 0,
@@ -54,9 +54,13 @@ typedef enum {
PS_MSGID = 1 << 5,
PS_MSGID_PLURAL = 1 << 6,
PS_MSGSTR = 1 << 7,
- PS_MSGSTR_ARRAY = 1 << 8
+ PS_MSGSTR_ARRAY = 1 << 8,
+ PS_STRING = 1 << 9 /* string literal */
} ParseState;
+#define PS_COMMENT \
+ ((PS_COMMENT_TRAN | PS_COMMENT_EXTR | PS_COMMENT_FLAG \
+ | PS_COMMENT_REFN | PS_COMMENT_OBSO))
/* Complete list at:
*
* https://www.gnu.org/software/gettext/manual/html_node/PO-Files.html
@@ -97,40 +101,35 @@ struct PoEntry {
size_t ref_comment_lines;
};
-char* format_flags(char* buffer, const size_t max,
- const struct PoEntry* entry, const int draw_box);
+char* format_flags(char* buffer, const size_t max, const struct PoEntry* entry,
+ const int draw_box);
void init_po_entry(struct PoEntry* entry);
void free_po_entry(struct PoEntry* entry);
struct PoEntry* set_flags(struct PoEntry* entry, const Flags flags);
-struct PoEntry* set_msgid(struct PoEntry* entry, const char* msgid,
- int append);
+struct PoEntry* set_msgid(struct PoEntry* entry, const char* msgid, int append);
struct PoEntry* set_msgid_plural(struct PoEntry* entry,
const char* msgid_plural, int append);
struct PoEntry* set_plural_forms(struct PoEntry* entry,
const char* plural_forms, int append);
struct PoEntry* set_msgstr(struct PoEntry* entry, const char* msgstr,
int msgstr_index, int append, struct DrawState* state);
-struct PoEntry* u32_set_msgid(struct PoEntry* entry,
- const uint32_t* msgid, size_t msgid_size, int append);
+struct PoEntry* u32_set_msgid(struct PoEntry* entry, const uint32_t* msgid,
+ size_t msgid_size, int append);
struct PoEntry* u32_set_msgid_plural(struct PoEntry* entry,
const uint32_t* msgid_plural, size_t msgid_plural_size, int append);
struct PoEntry* u32_set_plural_forms(struct PoEntry* entry,
const uint32_t* plural_forms, int append);
-struct PoEntry* u32_set_msgstr(struct PoEntry* entry,
- const uint32_t* msgstr, int msgstr_index, int append,
- struct DrawState* state);
-struct PoEntry* u32_add_comment(struct PoEntry* entry,
- const uint32_t* comment);
+struct PoEntry* u32_set_msgstr(struct PoEntry* entry, const uint32_t* msgstr,
+ int msgstr_index, int append, struct DrawState* state);
+struct PoEntry* u32_add_comment(struct PoEntry* entry, const uint32_t* comment);
struct PoEntry* u32_add_trans_comment(struct PoEntry* entry,
const uint32_t* trans_comment);
-struct PoEntry* add_ref_comment(struct PoEntry* entry,
- const char* comment);
+struct PoEntry* add_ref_comment(struct PoEntry* entry, const char* comment);
int load_file(struct DrawState* state, long* lineno, long* col);
int save_file(struct DrawState* state);
int parse_po_line(const char* line, struct DrawState* dstate,
struct PoEntry** current_entry, ParseState* pstate, long* col,
int* msgstr_index);
-void update_statistics(const struct PoEntry* entries,
- struct DrawState* state, struct PoEntry* entry);
-void update_warning(const uint32_t* msgid, const uint32_t* msgstr,
- int* warning);
+void update_statistics(const struct PoEntry* entries, struct DrawState* state,
+ struct PoEntry* entry);
+void update_warning(const uint32_t* msgid, const uint32_t* msgstr, int* warning);
diff --git a/poe.c b/poe.c
@@ -2670,10 +2670,19 @@ main_done:
if (saved_errno)
perror(PROGRAM_NAME);
- if (state.error == ERR_NOTREG_FILE)
+ switch (state.error)
+ {
+ case ERR_NOTREG_FILE:
print_error(1, errors[state.error], state.filename);
- else if (state.error)
+ break;
+ case ERR_SYNTAX:
+ print_error(1, errors[state.error], state.filename, lineno, col);
+ break;
+ case ERR_NONE:
+ break;
+ default:
print_error(1, errors[state.error]);
+ }
return saved_errno
? saved_errno