--- /dev/null
+// TortoiseSVN - a Windows shell extension for easy version control\r
+\r
+// Copyright (C) 2003-2008 - TortoiseSVN\r
+\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License\r
+// as published by the Free Software Foundation; either version 2\r
+// of the License, or (at your option) any later version.\r
+\r
+// This program is distributed in the hope that it will be useful,\r
+// but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+// GNU General Public License for more details.\r
+\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software Foundation,\r
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.\r
+#include "StdAfx.h"\r
+#include "shlwapi.h"\r
+#include <fstream>\r
+#include "codecvt.h"\r
+#include "Utils.h"\r
+#include "ResModule.h"\r
+#include ".\pofile.h"\r
+\r
+#define MYERROR {CUtils::Error(); return FALSE;}\r
+\r
+CPOFile::CPOFile()\r
+{\r
+}\r
+\r
+CPOFile::~CPOFile(void)\r
+{\r
+}\r
+\r
+BOOL CPOFile::ParseFile(LPCTSTR szPath, BOOL bUpdateExisting /* = TRUE */)\r
+{\r
+ if (!PathFileExists(szPath))\r
+ return FALSE;\r
+\r
+ if (!m_bQuiet)\r
+ _ftprintf(stdout, _T("parsing file %s...\n"), szPath);\r
+\r
+ int nEntries = 0;\r
+ int nDeleted = 0;\r
+ int nTranslated = 0;\r
+ //since stream classes still expect the filepath in char and not wchar_t\r
+ //we need to convert the filepath to multibyte\r
+ char filepath[MAX_PATH+1];\r
+ SecureZeroMemory(filepath, sizeof(filepath));\r
+ WideCharToMultiByte(CP_ACP, NULL, szPath, -1, filepath, MAX_PATH, NULL, NULL);\r
+\r
+ std::wifstream File;\r
+ File.imbue(std::locale(std::locale(), new utf8_conversion()));\r
+ File.open(filepath);\r
+ if (!File.good())\r
+ {\r
+ _ftprintf(stderr, _T("can't open input file %s\n"), szPath);\r
+ return FALSE;\r
+ }\r
+ TCHAR line[2*MAX_STRING_LENGTH];\r
+ std::vector<std::wstring> entry;\r
+ do\r
+ {\r
+ File.getline(line, sizeof(line)/sizeof(TCHAR));\r
+ if (line[0]==0)\r
+ {\r
+ //empty line means end of entry!\r
+ RESOURCEENTRY resEntry;\r
+ std::wstring msgid;\r
+ int type = 0;\r
+ for (std::vector<std::wstring>::iterator I = entry.begin(); I != entry.end(); ++I)\r
+ {\r
+ if (_tcsncmp(I->c_str(), _T("# "), 2)==0)\r
+ {\r
+ //user comment\r
+ resEntry.translatorcomments.push_back(I->c_str());\r
+ type = 0;\r
+ }\r
+ if (_tcsncmp(I->c_str(), _T("#."), 2)==0)\r
+ {\r
+ //automatic comments\r
+ resEntry.automaticcomments.push_back(I->c_str());\r
+ type = 0;\r
+ }\r
+ if (_tcsncmp(I->c_str(), _T("#,"), 2)==0)\r
+ {\r
+ //flag\r
+ resEntry.flag = I->c_str();\r
+ type = 0;\r
+ }\r
+ if (_tcsncmp(I->c_str(), _T("msgid"), 5)==0)\r
+ {\r
+ //message id\r
+ msgid = I->c_str();\r
+ msgid = std::wstring(msgid.substr(7, msgid.size() - 8));\r
+ nEntries++;\r
+ type = 1;\r
+ }\r
+ if (_tcsncmp(I->c_str(), _T("msgstr"), 6)==0)\r
+ {\r
+ //message string\r
+ resEntry.msgstr = I->c_str();\r
+ resEntry.msgstr = resEntry.msgstr.substr(8, resEntry.msgstr.length() - 9);\r
+ if (resEntry.msgstr.size()>0)\r
+ nTranslated++;\r
+ type = 2;\r
+ }\r
+ if (_tcsncmp(I->c_str(), _T("\""), 1)==0)\r
+ {\r
+ if (type == 1)\r
+ {\r
+ std::wstring temp = I->c_str();\r
+ temp = temp.substr(1, temp.length()-2);\r
+ msgid += temp;\r
+ }\r
+ if (type == 2)\r
+ {\r
+ if (resEntry.msgstr.size() == 0)\r
+ nTranslated++;\r
+ std::wstring temp = I->c_str();\r
+ temp = temp.substr(1, temp.length()-2);\r
+ resEntry.msgstr += temp;\r
+ }\r
+ }\r
+ }\r
+ entry.clear();\r
+ if ((bUpdateExisting)&&(this->count(msgid) == 0))\r
+ nDeleted++;\r
+ else\r
+ (*this)[msgid] = resEntry;\r
+ msgid.clear();\r
+ }\r
+ else\r
+ {\r
+ entry.push_back(line);\r
+ }\r
+ } while (File.gcount() > 0);\r
+ printf(File.getloc().name().c_str());\r
+ File.close();\r
+ RESOURCEENTRY emptyentry;\r
+ (*this)[std::wstring(_T(""))] = emptyentry;\r
+ if (!m_bQuiet)\r
+ _ftprintf(stdout, _T("%d Entries found, %d were already translated and %d got deleted\n"), nEntries, nTranslated, nDeleted);\r
+ return TRUE;\r
+}\r
+\r
+BOOL CPOFile::SaveFile(LPCTSTR szPath)\r
+{\r
+ //since stream classes still expect the filepath in char and not wchar_t\r
+ //we need to convert the filepath to multibyte\r
+ char filepath[MAX_PATH+1];\r
+ int nEntries = 0;\r
+ SecureZeroMemory(filepath, sizeof(filepath));\r
+ WideCharToMultiByte(CP_ACP, NULL, szPath, -1, filepath, MAX_PATH, NULL, NULL);\r
+\r
+ std::wofstream File;\r
+// File.open(filepath, std::ios_base::binary);\r
+// File << _T("\xEF\xBB\xBF");\r
+// File.close();\r
+ File.imbue(std::locale(std::locale(), new utf8_conversion()));\r
+ File.open(filepath, std::ios_base::binary);\r
+ File << _T("# SOME DESCRIPTIVE TITLE.\n");\r
+ File << _T("# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n");\r
+ File << _T("# This file is distributed under the same license as the PACKAGE package.\n");\r
+ File << _T("# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n");\r
+ File << _T("#\n");\r
+ File << _T("#, fuzzy\n");\r
+ File << _T("msgid \"\"\n");\r
+ File << _T("msgstr \"\"\n");\r
+ File << _T("\"Project-Id-Version: PACKAGE VERSION\\n\"\n");\r
+ File << _T("\"Report-Msgid-Bugs-To: \\n\"\n");\r
+ File << _T("\"POT-Creation-Date: 1900-01-01 00:00+0000\\n\"\n");\r
+ File << _T("\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n");\r
+ File << _T("\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n");\r
+ File << _T("\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n");\r
+ File << _T("\"MIME-Version: 1.0\\n\"\n");\r
+ File << _T("\"Content-Type: text/plain; charset=UTF-8\\n\"\n");\r
+ File << _T("\"Content-Transfer-Encoding: 8bit\\n\"\n\n");\r
+ File << _T("\n");\r
+ File << _T("# msgid/msgstr fields for Accelerator keys\n");\r
+ File << _T("# Format is: \"ID:xxxxxx:VACS+X\" where:\n");\r
+ File << _T("# ID:xxxxx = the menu ID corresponding to the accelerator\n");\r
+ File << _T("# V = Virtual key (or blank if not used) - nearly always set!\n");\r
+ File << _T("# A = Alt key (or blank if not used)\n");\r
+ File << _T("# C = Ctrl key (or blank if not used)\n");\r
+ File << _T("# S = Shift key (or blank if not used)\n");\r
+ File << _T("# X = upper case character\n");\r
+ File << _T("# e.g. \"V CS+Q\" == Ctrl + Shift + 'Q'\n");\r
+ File << _T("\n");\r
+ File << _T("# ONLY Accelerator Keys with corresponding alphanumeric characters can be\n");\r
+ File << _T("# updated i.e. function keys (F2), special keys (Delete, HoMe) etc. will not.\n");\r
+ File << _T("\n");\r
+ File << _T("# ONLY change the msgstr field. Do NOT change any other.\n");\r
+ File << _T("# If you do not want to change an Accelerator Key, copy msgid to msgstr\n");\r
+ File << _T("\n");\r
+\r
+ for (std::map<std::wstring, RESOURCEENTRY>::iterator I = this->begin(); I != this->end(); ++I)\r
+ {\r
+ if (I->first.size() == 0)\r
+ continue;\r
+ RESOURCEENTRY entry = I->second;\r
+ for (std::vector<std::wstring>::iterator II = entry.automaticcomments.begin(); II != entry.automaticcomments.end(); ++II)\r
+ {\r
+ File << II->c_str() << _T("\n");\r
+ }\r
+ for (std::vector<std::wstring>::iterator II = entry.translatorcomments.begin(); II != entry.translatorcomments.end(); ++II)\r
+ {\r
+ File << II->c_str() << _T("\n");\r
+ }\r
+ if (I->second.resourceIDs.size() > 0)\r
+ {\r
+ File << _T("#. Resource IDs: (");\r
+\r
+ std::set<DWORD>::const_iterator II = I->second.resourceIDs.begin();\r
+ File << (*II);\r
+ ++II;\r
+ while (II != I->second.resourceIDs.end())\r
+ {\r
+ File << _T(", ");\r
+ File << (*II);\r
+ ++II;\r
+ };\r
+ File << _T(")\n");\r
+ }\r
+ if (I->second.flag.length() > 0)\r
+ File << (I->second.flag.c_str()) << _T("\n");\r
+ File << (_T("msgid \"")) << (I->first.c_str()) << _T("\"\n");\r
+ File << (_T("msgstr \"")) << (I->second.msgstr.c_str()) << _T("\"\n\n");\r
+ nEntries++;\r
+ }\r
+ File.close();\r
+ if (!m_bQuiet)\r
+ _ftprintf(stdout, _T("File %s saved, containing %d entries\n"), szPath, nEntries);\r
+ return TRUE;\r
+}\r