Initial commit
This commit is contained in:
221
devtools/create_supernova/po_parser.cpp
Normal file
221
devtools/create_supernova/po_parser.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "po_parser.h"
|
||||
|
||||
PoMessageList::PoMessageList() : _list(nullptr), _size(0), _allocated(0) {
|
||||
}
|
||||
|
||||
PoMessageList::~PoMessageList() {
|
||||
for (int i = 0; i < _size; ++i)
|
||||
delete _list[i];
|
||||
delete[] _list;
|
||||
}
|
||||
|
||||
int PoMessageList::compareString(const char* left, const char* right) {
|
||||
if (left == nullptr && right == nullptr)
|
||||
return 0;
|
||||
if (left == nullptr)
|
||||
return -1;
|
||||
if (right == nullptr)
|
||||
return 1;
|
||||
return strcmp(left, right);
|
||||
}
|
||||
|
||||
int PoMessageList::compareMessage(const char *msgLeft, const char *contextLeft, const char *msgRight, const char *contextRight) {
|
||||
int compare = compareString(msgLeft, msgRight);
|
||||
if (compare != 0)
|
||||
return compare;
|
||||
return compareString(contextLeft, contextRight);
|
||||
}
|
||||
|
||||
void PoMessageList::insert(const char *translation, const char *msg, const char *context) {
|
||||
if (msg == nullptr || *msg == '\0' || translation == nullptr || *translation == '\0')
|
||||
return;
|
||||
|
||||
// binary-search for the insertion index
|
||||
int leftIndex = 0;
|
||||
int rightIndex = _size - 1;
|
||||
while (rightIndex >= leftIndex) {
|
||||
int midIndex = (leftIndex + rightIndex) / 2;
|
||||
int compareResult = compareMessage(msg, context, _list[midIndex]->msgid, _list[midIndex]->msgctxt);
|
||||
if (compareResult == 0)
|
||||
return; // The message is already in this list
|
||||
else if (compareResult < 0)
|
||||
rightIndex = midIndex - 1;
|
||||
else
|
||||
leftIndex = midIndex + 1;
|
||||
}
|
||||
// We now have rightIndex = leftIndex - 1 and we need to insert the new message
|
||||
// between the two (i.a. at leftIndex).
|
||||
if (_size + 1 > _allocated) {
|
||||
_allocated += 100;
|
||||
PoMessage **newList = new PoMessage*[_allocated];
|
||||
for (int i = 0; i < leftIndex; ++i)
|
||||
newList[i] = _list[i];
|
||||
for (int i = leftIndex; i < _size; ++i)
|
||||
newList[i + 1] = _list[i];
|
||||
delete[] _list;
|
||||
_list = newList;
|
||||
} else {
|
||||
for (int i = _size - 1; i >= leftIndex; --i)
|
||||
_list[i + 1] = _list[i];
|
||||
}
|
||||
_list[leftIndex] = new PoMessage(translation, msg, context);
|
||||
++_size;
|
||||
}
|
||||
|
||||
const char *PoMessageList::findTranslation(const char *msg, const char *context) {
|
||||
if (msg == nullptr || *msg == '\0')
|
||||
return nullptr;
|
||||
|
||||
// binary-search for the message
|
||||
int leftIndex = 0;
|
||||
int rightIndex = _size - 1;
|
||||
while (rightIndex >= leftIndex) {
|
||||
int midIndex = (leftIndex + rightIndex) / 2;
|
||||
int compareResult = compareMessage(msg, context, _list[midIndex]->msgid, _list[midIndex]->msgctxt);
|
||||
if (compareResult == 0)
|
||||
return _list[midIndex]->msgstr;
|
||||
else if (compareResult < 0)
|
||||
rightIndex = midIndex - 1;
|
||||
else
|
||||
leftIndex = midIndex + 1;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PoMessageList *parsePoFile(const char *file) {
|
||||
FILE *inFile = fopen(file, "r");
|
||||
if (!inFile)
|
||||
return nullptr;
|
||||
|
||||
char msgidBuf[1024], msgctxtBuf[1024], msgstrBuf[1024];
|
||||
char line[1024], *currentBuf = msgstrBuf;
|
||||
|
||||
PoMessageList *list = new PoMessageList();
|
||||
|
||||
// Initialize the message attributes.
|
||||
bool fuzzy = false;
|
||||
bool fuzzy_next = false;
|
||||
|
||||
// Parse the file line by line.
|
||||
// The msgstr is always the last line of an entry (i.e. msgid and msgctxt always
|
||||
// precede the corresponding msgstr).
|
||||
msgidBuf[0] = msgstrBuf[0] = msgctxtBuf[0] = '\0';
|
||||
while (!feof(inFile) && fgets(line, 1024, inFile)) {
|
||||
if (line[0] == '#' && line[1] == ',') {
|
||||
// Handle message attributes.
|
||||
if (strstr(line, "fuzzy")) {
|
||||
fuzzy_next = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// Skip empty and comment line
|
||||
if (*line == '\n' || *line == '#')
|
||||
continue;
|
||||
if (strncmp(line, "msgid", 5) == 0) {
|
||||
if (currentBuf == msgstrBuf) {
|
||||
// add previous entry
|
||||
if (*msgstrBuf != '\0' && !fuzzy)
|
||||
list->insert(msgstrBuf, msgidBuf, msgctxtBuf);
|
||||
msgidBuf[0] = msgstrBuf[0] = msgctxtBuf[0] = '\0';
|
||||
|
||||
// Reset the attribute flags.
|
||||
fuzzy = fuzzy_next;
|
||||
fuzzy_next = false;
|
||||
}
|
||||
strcpy(msgidBuf, stripLine(line));
|
||||
currentBuf = msgidBuf;
|
||||
} else if (strncmp(line, "msgctxt", 7) == 0) {
|
||||
if (currentBuf == msgstrBuf) {
|
||||
// add previous entry
|
||||
if (*msgstrBuf != '\0' && !fuzzy)
|
||||
list->insert(msgstrBuf, msgidBuf, msgctxtBuf);
|
||||
msgidBuf[0] = msgstrBuf[0] = msgctxtBuf[0] = '\0';
|
||||
|
||||
// Reset the attribute flags
|
||||
fuzzy = fuzzy_next;
|
||||
fuzzy_next = false;
|
||||
}
|
||||
strcpy(msgctxtBuf, stripLine(line));
|
||||
currentBuf = msgctxtBuf;
|
||||
} else if (strncmp(line, "msgstr", 6) == 0) {
|
||||
strcpy(msgstrBuf, stripLine(line));
|
||||
currentBuf = msgstrBuf;
|
||||
} else {
|
||||
// concatenate the string at the end of the current buffer
|
||||
if (currentBuf)
|
||||
strcat(currentBuf, stripLine(line));
|
||||
}
|
||||
}
|
||||
if (currentBuf == msgstrBuf) {
|
||||
// add last entry
|
||||
if (*msgstrBuf != '\0' && !fuzzy)
|
||||
list->insert(msgstrBuf, msgidBuf, msgctxtBuf);
|
||||
}
|
||||
|
||||
fclose(inFile);
|
||||
return list;
|
||||
}
|
||||
|
||||
char *stripLine(char *const line) {
|
||||
// This function modifies line in place and return it.
|
||||
// Keep only the text between the first two unprotected quotes.
|
||||
// It also look for literal special characters (e.g. preceded by '\n', '\\', '\"', '\'', '\t')
|
||||
// and replace them by the special character so that strcmp() can match them at run time.
|
||||
// Look for the first quote
|
||||
char const *src = line;
|
||||
while (*src != '\0' && *src++ != '"') {}
|
||||
// shift characters until we reach the end of the string or an unprotected quote
|
||||
char *dst = line;
|
||||
while (*src != '\0' && *src != '"') {
|
||||
char c = *src++;
|
||||
if (c == '\\') {
|
||||
switch (c = *src++) {
|
||||
case 'n': c = '\n'; break;
|
||||
case 't': c = '\t'; break;
|
||||
case '\"': c = '\"'; break;
|
||||
case '\'': c = '\''; break;
|
||||
case '\\': c = '\\'; break;
|
||||
default:
|
||||
// Just skip
|
||||
fprintf(stderr, "Unsupported special character \"\\%c\" in string. Please contact ScummVM developers.\n", c);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
*dst++ = c;
|
||||
}
|
||||
*dst = '\0';
|
||||
return line;
|
||||
}
|
||||
|
||||
char *parseLine(const char *line, const char *field) {
|
||||
// This function allocate and return a new char*.
|
||||
// It will return a NULL pointer if the field is not found.
|
||||
// It is used to parse the header of the po files to find the language name
|
||||
// and the charset.
|
||||
const char *str = strstr(line, field);
|
||||
if (str == nullptr)
|
||||
return nullptr;
|
||||
str += strlen(field);
|
||||
// Skip spaces
|
||||
while (*str != '\0' && isspace(*str)) {
|
||||
++str;
|
||||
}
|
||||
// Find string length (stop at the first '\n')
|
||||
int len = 0;
|
||||
while (str[len] != '\0' && str[len] != '\n') {
|
||||
++len;
|
||||
}
|
||||
if (len == 0)
|
||||
return nullptr;
|
||||
// Create result string
|
||||
char *result = new char[len + 1];
|
||||
strncpy(result, str, len);
|
||||
result[len] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user