/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * This is a utility for create the translations.dat file from all the po files. * The generated files is used by ScummVM to propose translation of its GUI. */ #include #include #include #include #include "po_parser.h" PoMessageList::PoMessageList() : _messages(NULL), _size(0), _allocated(0) { } PoMessageList::~PoMessageList() { for (int i = 0 ; i < _size ; ++i) delete [] _messages[i]; delete [] _messages; } void PoMessageList::insert(const char *msg) { if (msg == NULL || *msg == '\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 = strcmp(msg, _messages[midIndex]); if (compareResult == 0) return; 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; char** newMessages = new char*[_allocated]; for (int i = 0 ; i < leftIndex ; ++i) newMessages[i] = _messages[i]; for (int i = leftIndex ; i < _size ; ++i) newMessages[i+1] = _messages[i]; delete [] _messages; _messages = newMessages; } else { for (int i = _size - 1 ; i >= leftIndex ; --i) _messages[i+1] = _messages[i]; } _messages[leftIndex] = new char[1+strlen(msg)]; strcpy(_messages[leftIndex], msg); ++_size; } int PoMessageList::findIndex(const char *msg) { if (msg == NULL || *msg == '\0') return -1; // binary-search for the message int leftIndex = 0; int rightIndex = _size - 1; while (rightIndex >= leftIndex) { const int midIndex = (leftIndex + rightIndex) / 2; const int compareResult = strcmp(msg, _messages[midIndex]); if (compareResult == 0) return midIndex; else if (compareResult < 0) rightIndex = midIndex - 1; else leftIndex = midIndex + 1; } return -1; } int PoMessageList::size() const { return _size; } const char* PoMessageList::operator[](int index) const { if (index < 0 || index >= _size) return NULL; return _messages[index]; } PoMessageEntryList::PoMessageEntryList(const char* lang) : _lang(NULL), _charset(NULL), _langName(NULL), _list(NULL), _size(0), _allocated(0) { _lang = new char[1 + strlen(lang)]; strcpy(_lang, lang); // Set default charset to empty string _charset = new char[1]; _charset[0] = '\0'; // Set default langName to lang _langName = new char[1 + strlen(lang)]; strcpy(_langName, lang); } PoMessageEntryList::~PoMessageEntryList() { delete [] _lang; delete [] _charset; delete [] _langName; for (int i = 0 ; i < _size ; ++i) delete _list[i]; delete [] _list; } void PoMessageEntryList::addMessageEntry(const char *translation, const char *message, const char *context) { if (*message == '\0') { // This is the header. // We get the charset and the language name from the translation string char* str = parseLine(translation, "Language:"); if (str != NULL) { delete [] _langName; _langName = str; } str = parseLine(translation, "charset="); if (str != NULL) { delete [] _charset; _charset = str; } return; } // binary-search for the insertion index int leftIndex = 0; int rightIndex = _size - 1; while (rightIndex >= leftIndex) { int midIndex = (leftIndex + rightIndex) / 2; int compareResult = strcmp(message, _list[midIndex]->msgid); if (compareResult == 0) { if (context == NULL) { if (_list[midIndex]->msgctxt == NULL) return; compareResult = -1; } else { if (_list[midIndex]->msgctxt == NULL) compareResult = 1; else { compareResult = strcmp(context, _list[midIndex]->msgctxt); if (compareResult == 0) return; } } } 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; PoMessageEntry** newList = new PoMessageEntry*[_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 PoMessageEntry(translation, message, context); ++_size; } const char* PoMessageEntryList::language() const { return _lang; } const char* PoMessageEntryList::languageName() const { return _langName; } const char* PoMessageEntryList::charset() const { return _charset; } int PoMessageEntryList::size() const { return _size; } const PoMessageEntry* PoMessageEntryList::entry(int index) const { if (index < 0 || index >= _size) return NULL; return _list[index]; } PoMessageEntryList* parsePoFile(const char* file, PoMessageList& messages) { FILE* inFile = fopen(file, "r"); if (!inFile) return NULL; char msgidBuf[1024], msgctxtBuf[1024], msgstrBuf[1024]; char line[1024], *currentBuf = NULL; // Get language from file name and create PoMessageEntryList int index = 0, start_index = strlen(file) - 1; while (start_index > 0 && file[start_index-1] != '/' && file[start_index-1] != '\\') { --start_index; } while (file[start_index+index] != '.' && file[start_index+index] != '\0') { msgidBuf[index] = file[start_index+index]; ++index; } msgidBuf[index] = '\0'; PoMessageEntryList *list = new PoMessageEntryList(msgidBuf); // 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)) { // 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') { messages.insert(msgidBuf); list->addMessageEntry(msgstrBuf, msgidBuf, msgctxtBuf); } msgidBuf[0] = msgstrBuf[0] = msgctxtBuf[0] = '\0'; } strcpy(msgidBuf, stripLine(line)); currentBuf = msgidBuf; } else if (strncmp(line, "msgctxt", 7) == 0) { if (currentBuf == msgstrBuf) { // add previous entry if (*msgstrBuf != '\0') { messages.insert(msgidBuf); list->addMessageEntry(msgstrBuf, msgidBuf, msgctxtBuf); } msgidBuf[0] = msgstrBuf[0] = msgctxtBuf[0] = '\0'; } 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)); } } fclose(inFile); return list; } char* stripLine(char* line) { // This function modifies line in place and return it. // Keep only the text between the first two unprotected quotes. // Look for the first quote int start = 0; int len = strlen(line); while (start < len && line[start++] != '"') {} // shift characters until we reach the end of the string or an unprotected quote int i = 0; while (start+i < len && (line[start+i] != '"' || (i > 0 && line[start+i-1] == '\\'))) { line[i] = line[start+i]; ++i; } line[i] = '\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. char* str = strstr(line, field); if (str == NULL) return NULL; str += strlen(field); // Skip spaces while (*str != '\0' && isspace(*str)) { ++str; } // Find string length (top at the first '\\' // (since the string we want is followed by a '\\n') int len = 0; while (str[len] != '\0' && str[len] != '\\') { ++len; } if (len == 0) return NULL; // Create result string char* result = new char[len+1]; strncpy(result, str, len); result[len] = '\0'; return result; }