1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2013 - TortoiseGit
4 // Copyright (C) 2003-2008, 2011-2016 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "ResModule.h"
32 #define MYERROR {CUtils::Error(); return FALSE;}
36 , m_bAdjustEOLs(false)
40 CPOFile::~CPOFile(void)
44 BOOL
CPOFile::ParseFile(LPCTSTR szPath
, BOOL bUpdateExisting
, bool bAdjustEOLs
)
46 if (!PathFileExists(szPath
))
49 m_bAdjustEOLs
= bAdjustEOLs
;
52 _ftprintf(stdout
, L
"parsing file %s...\n", szPath
);
57 //since stream classes still expect the filepath in char and not wchar_t
58 //we need to convert the filepath to multibyte
59 char filepath
[MAX_PATH
+ 1] = { 0 };
60 WideCharToMultiByte(CP_ACP
, 0, szPath
, -1, filepath
, _countof(filepath
) - 1, nullptr, nullptr);
63 File
.imbue(std::locale(std::locale(), new utf8_conversion()));
67 _ftprintf(stderr
, L
"can't open input file %s\n", szPath
);
70 auto line
= std::make_unique
<TCHAR
[]>(2 * MAX_STRING_LENGTH
);
71 std::vector
<std::wstring
> entry
;
74 File
.getline(line
.get(), 2*MAX_STRING_LENGTH
);
77 //empty line means end of entry!
78 RESOURCEENTRY resEntry
= {0};
81 for (auto I
= entry
.cbegin(); I
!= entry
.cend(); ++I
)
83 if (wcsncmp(I
->c_str(), L
"# ", 2)==0)
86 resEntry
.translatorcomments
.push_back(I
->c_str());
89 if (wcsncmp(I
->c_str(), L
"#.", 2)==0)
92 resEntry
.automaticcomments
.push_back(I
->c_str());
95 if (wcsncmp(I
->c_str(), L
"#,", 2)==0)
98 resEntry
.flag
= I
->c_str();
101 if (wcsncmp(I
->c_str(), L
"msgid", 5)==0)
105 msgid
= std::wstring(msgid
.substr(7, msgid
.size() - 8));
107 std::wstring s
= msgid
;
108 s
.erase(s
.cbegin(), std::find_if(s
.cbegin(), s
.cend(), std::not1(std::ptr_fun
<wint_t, int>(iswspace
))));
113 if (wcsncmp(I
->c_str(), L
"msgstr", 6)==0)
116 resEntry
.msgstr
= I
->c_str();
117 resEntry
.msgstr
= resEntry
.msgstr
.substr(8, resEntry
.msgstr
.length() - 9);
118 if (!resEntry
.msgstr
.empty())
122 if (wcsncmp(I
->c_str(), L
"\"", 1)==0)
126 std::wstring temp
= I
->c_str();
127 temp
= temp
.substr(1, temp
.length()-2);
132 if (resEntry
.msgstr
.empty())
134 std::wstring temp
= I
->c_str();
135 temp
= temp
.substr(1, temp
.length()-2);
136 resEntry
.msgstr
+= temp
;
141 if ((bUpdateExisting
)&&(this->count(msgid
) == 0))
145 if ((m_bAdjustEOLs
)&&(msgid
.find(L
"\\r\\n") != std::string::npos
))
147 AdjustEOLs(resEntry
.msgstr
);
149 // always use the new data for generated comments/flags
150 auto newEntry
= (*this)[msgid
];
151 resEntry
.automaticcomments
= newEntry
.automaticcomments
;
152 resEntry
.flag
= newEntry
.flag
;
153 resEntry
.resourceIDs
= newEntry
.resourceIDs
;
155 (*this)[msgid
] = resEntry
;
161 entry
.push_back(line
.get());
163 } while (File
.gcount() > 0);
164 printf("%s", File
.getloc().name().c_str());
166 RESOURCEENTRY emptyentry
= {0};
167 (*this)[std::wstring(L
"")] = emptyentry
;
169 _ftprintf(stdout
, L
"%d Entries found, %d were already translated and %d got deleted\n", nEntries
, nTranslated
, nDeleted
);
173 BOOL
CPOFile::SaveFile(LPCTSTR szPath
, LPCTSTR lpszHeaderFile
)
175 //since stream classes still expect the filepath in char and not wchar_t
176 //we need to convert the filepath to multibyte
177 char filepath
[MAX_PATH
+ 1] = { 0 };
179 WideCharToMultiByte(CP_ACP
, 0, szPath
, -1, filepath
, _countof(filepath
) - 1, nullptr, nullptr);
182 File
.imbue(std::locale(std::locale(), new utf8_conversion()));
183 File
.open(filepath
, std::ios_base::binary
);
185 if ((lpszHeaderFile
)&&(lpszHeaderFile
[0])&&(PathFileExists(lpszHeaderFile
)))
187 // read the header file and save it to the top of the pot file
188 std::wifstream inFile
;
189 inFile
.imbue(std::locale(std::locale(), new utf8_conversion()));
190 inFile
.open(lpszHeaderFile
, std::ios_base::binary
);
193 while(inFile
&& inFile
.get(ch
))
199 File
<< L
"# SOME DESCRIPTIVE TITLE.\n";
200 File
<< L
"# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n";
201 File
<< L
"# This file is distributed under the same license as the PACKAGE package.\n";
202 File
<< L
"# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n";
204 File
<< L
"#, fuzzy\n";
205 File
<< L
"msgid \"\"\n";
206 File
<< L
"msgstr \"\"\n";
207 File
<< L
"\"Project-Id-Version: PACKAGE VERSION\\n\"\n";
208 File
<< L
"\"Report-Msgid-Bugs-To: \\n\"\n";
209 File
<< L
"\"POT-Creation-Date: 1900-01-01 00:00+0000\\n\"\n";
210 File
<< L
"\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n";
211 File
<< L
"\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n";
212 File
<< L
"\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n";
213 File
<< L
"\"MIME-Version: 1.0\\n\"\n";
214 File
<< L
"\"Content-Type: text/plain; charset=UTF-8\\n\"\n";
215 File
<< L
"\"Content-Transfer-Encoding: 8bit\\n\"\n\n";
218 File
<< L
"# msgid/msgstr fields for Accelerator keys\n";
219 File
<< L
"# Format is: \"ID:xxxxxx:VACS+X\" where:\n";
220 File
<< L
"# ID:xxxxx = the menu ID corresponding to the accelerator\n";
221 File
<< L
"# V = Virtual key (or blank if not used) - nearly always set!\n";
222 File
<< L
"# A = Alt key (or blank if not used)\n";
223 File
<< L
"# C = Ctrl key (or blank if not used)\n";
224 File
<< L
"# S = Shift key (or blank if not used)\n";
225 File
<< L
"# X = upper case character\n";
226 File
<< L
"# e.g. \"V CS+Q\" == Ctrl + Shift + 'Q'\n";
228 File
<< L
"# ONLY Accelerator Keys with corresponding alphanumeric characters can be\n";
229 File
<< L
"# updated i.e. function keys (F2), special keys (Delete, HoMe) etc. will not.\n";
231 File
<< L
"# ONLY change the msgstr field. Do NOT change any other.\n";
232 File
<< L
"# If you do not want to change an Accelerator Key, copy msgid to msgstr\n";
235 for (auto I
= this->cbegin(); I
!= this->cend(); ++I
)
237 std::wstring s
= I
->first
;
238 s
.erase(s
.cbegin(), std::find_if(s
.cbegin(), s
.cend(), std::not1(std::ptr_fun
<wint_t, int>(iswspace
))));
242 RESOURCEENTRY entry
= I
->second
;
243 for (auto II
= entry
.automaticcomments
.cbegin(); II
!= entry
.automaticcomments
.cend(); ++II
)
245 File
<< II
->c_str() << L
"\n";
247 for (auto II
= entry
.translatorcomments
.cbegin(); II
!= entry
.translatorcomments
.cend(); ++II
)
249 File
<< II
->c_str() << L
"\n";
251 if (!I
->second
.resourceIDs
.empty())
253 File
<< L
"#. Resource IDs: (";
255 auto II
= I
->second
.resourceIDs
.begin();
258 while (II
!= I
->second
.resourceIDs
.end())
266 if (I
->second
.flag
.length() > 0)
267 File
<< (I
->second
.flag
.c_str()) << L
"\n";
268 File
<< (L
"msgid \"") << (I
->first
.c_str()) << L
"\"\n";
269 File
<< (L
"msgstr \"") << (I
->second
.msgstr
.c_str()) << L
"\"\n\n";
274 _ftprintf(stdout
, L
"File %s saved, containing %d entries\n", szPath
, nEntries
);
278 void CPOFile::AdjustEOLs(std::wstring
& str
)
281 std::wstring::size_type pos
= 0;
282 for ( ; ; ) // while (true)
284 std::wstring::size_type next
= str
.find(L
"\\r\\n", pos
);
285 result
.append(str
, pos
, next
-pos
);
286 if( next
!= std::string::npos
)
288 result
.append(L
"\\n");
289 pos
= next
+ 4; // 4 = sizeof("\\r\\n")
300 for ( ; ; ) // while (true)
302 std::wstring::size_type next
= str
.find(L
"\\n", pos
);
303 result
.append(str
, pos
, next
-pos
);
304 if( next
!= std::string::npos
)
306 result
.append(L
"\\r\\n");
307 pos
= next
+ 2; // 2 = sizeof("\\n")