1
// TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2012-2013 - TortoiseGit
4 // Copyright (C) 2003-2008, 2011-2015 - 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
, _T("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];
60 SecureZeroMemory(filepath
, sizeof(filepath
));
61 WideCharToMultiByte(CP_ACP
, NULL
, szPath
, -1, filepath
, _countof(filepath
)-1, NULL
, NULL
);
64 File
.imbue(std::locale(std::locale(), new utf8_conversion()));
68 _ftprintf(stderr
, _T("can't open input file %s\n"), szPath
);
71 std::unique_ptr
<TCHAR
[]> line(new TCHAR
[2*MAX_STRING_LENGTH
]);
72 std::vector
<std::wstring
> entry
;
75 File
.getline(line
.get(), 2*MAX_STRING_LENGTH
);
78 //empty line means end of entry!
79 RESOURCEENTRY resEntry
;
82 for (std::vector
<std::wstring
>::iterator I
= entry
.begin(); I
!= entry
.end(); ++I
)
84 if (_tcsncmp(I
->c_str(), _T("# "), 2)==0)
87 resEntry
.translatorcomments
.push_back(I
->c_str());
90 if (_tcsncmp(I
->c_str(), _T("#."), 2)==0)
93 resEntry
.automaticcomments
.push_back(I
->c_str());
96 if (_tcsncmp(I
->c_str(), _T("#,"), 2)==0)
99 resEntry
.flag
= I
->c_str();
102 if (_tcsncmp(I
->c_str(), _T("msgid"), 5)==0)
106 msgid
= std::wstring(msgid
.substr(7, msgid
.size() - 8));
108 std::wstring s
= msgid
;
109 s
.erase(s
.begin(), std::find_if(s
.begin(), s
.end(), std::not1(std::ptr_fun
<wint_t, int>(iswspace
))));
114 if (_tcsncmp(I
->c_str(), _T("msgstr"), 6)==0)
117 resEntry
.msgstr
= I
->c_str();
118 resEntry
.msgstr
= resEntry
.msgstr
.substr(8, resEntry
.msgstr
.length() - 9);
119 if (!resEntry
.msgstr
.empty())
123 if (_tcsncmp(I
->c_str(), _T("\""), 1)==0)
127 std::wstring temp
= I
->c_str();
128 temp
= temp
.substr(1, temp
.length()-2);
133 if (resEntry
.msgstr
.empty())
135 std::wstring temp
= I
->c_str();
136 temp
= temp
.substr(1, temp
.length()-2);
137 resEntry
.msgstr
+= temp
;
142 if ((bUpdateExisting
)&&(this->count(msgid
) == 0))
146 if ((m_bAdjustEOLs
)&&(msgid
.find(L
"\\r\\n") != std::string::npos
))
148 AdjustEOLs(resEntry
.msgstr
);
150 // always use the new data for generated comments/flags
151 auto newEntry
= (*this)[msgid
];
152 resEntry
.automaticcomments
= newEntry
.automaticcomments
;
153 resEntry
.flag
= newEntry
.flag
;
154 resEntry
.resourceIDs
= newEntry
.resourceIDs
;
156 (*this)[msgid
] = resEntry
;
162 entry
.push_back(line
.get());
164 } while (File
.gcount() > 0);
165 printf(File
.getloc().name().c_str());
167 RESOURCEENTRY emptyentry
= {0};
168 (*this)[std::wstring(_T(""))] = emptyentry
;
170 _ftprintf(stdout
, _T("%d Entries found, %d were already translated and %d got deleted\n"), nEntries
, nTranslated
, nDeleted
);
174 BOOL
CPOFile::SaveFile(LPCTSTR szPath
, LPCTSTR lpszHeaderFile
)
176 //since stream classes still expect the filepath in char and not wchar_t
177 //we need to convert the filepath to multibyte
178 char filepath
[MAX_PATH
+1];
180 SecureZeroMemory(filepath
, sizeof(filepath
));
181 WideCharToMultiByte(CP_ACP
, NULL
, szPath
, -1, filepath
, _countof(filepath
)-1, NULL
, NULL
);
184 File
.imbue(std::locale(std::locale(), new utf8_conversion()));
185 File
.open(filepath
, std::ios_base::binary
);
187 if ((lpszHeaderFile
)&&(lpszHeaderFile
[0])&&(PathFileExists(lpszHeaderFile
)))
189 // read the header file and save it to the top of the pot file
190 std::wifstream inFile
;
191 inFile
.imbue(std::locale(std::locale(), new utf8_conversion()));
192 inFile
.open(lpszHeaderFile
, std::ios_base::binary
);
195 while(inFile
&& inFile
.get(ch
))
201 File
<< _T("# SOME DESCRIPTIVE TITLE.\n");
202 File
<< _T("# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n");
203 File
<< _T("# This file is distributed under the same license as the PACKAGE package.\n");
204 File
<< _T("# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n");
206 File
<< _T("#, fuzzy\n");
207 File
<< _T("msgid \"\"\n");
208 File
<< _T("msgstr \"\"\n");
209 File
<< _T("\"Project-Id-Version: PACKAGE VERSION\\n\"\n");
210 File
<< _T("\"Report-Msgid-Bugs-To: \\n\"\n");
211 File
<< _T("\"POT-Creation-Date: 1900-01-01 00:00+0000\\n\"\n");
212 File
<< _T("\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n");
213 File
<< _T("\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n");
214 File
<< _T("\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n");
215 File
<< _T("\"MIME-Version: 1.0\\n\"\n");
216 File
<< _T("\"Content-Type: text/plain; charset=UTF-8\\n\"\n");
217 File
<< _T("\"Content-Transfer-Encoding: 8bit\\n\"\n\n");
220 File
<< _T("# msgid/msgstr fields for Accelerator keys\n");
221 File
<< _T("# Format is: \"ID:xxxxxx:VACS+X\" where:\n");
222 File
<< _T("# ID:xxxxx = the menu ID corresponding to the accelerator\n");
223 File
<< _T("# V = Virtual key (or blank if not used) - nearly always set!\n");
224 File
<< _T("# A = Alt key (or blank if not used)\n");
225 File
<< _T("# C = Ctrl key (or blank if not used)\n");
226 File
<< _T("# S = Shift key (or blank if not used)\n");
227 File
<< _T("# X = upper case character\n");
228 File
<< _T("# e.g. \"V CS+Q\" == Ctrl + Shift + 'Q'\n");
230 File
<< _T("# ONLY Accelerator Keys with corresponding alphanumeric characters can be\n");
231 File
<< _T("# updated i.e. function keys (F2), special keys (Delete, HoMe) etc. will not.\n");
233 File
<< _T("# ONLY change the msgstr field. Do NOT change any other.\n");
234 File
<< _T("# If you do not want to change an Accelerator Key, copy msgid to msgstr\n");
237 for (std::map
<std::wstring
, RESOURCEENTRY
>::iterator I
= this->begin(); I
!= this->end(); ++I
)
239 std::wstring s
= I
->first
;
240 s
.erase(s
.begin(), std::find_if(s
.begin(), s
.end(), std::not1(std::ptr_fun
<wint_t, int>(iswspace
))));
244 RESOURCEENTRY entry
= I
->second
;
245 for (std::vector
<std::wstring
>::iterator II
= entry
.automaticcomments
.begin(); II
!= entry
.automaticcomments
.end(); ++II
)
247 File
<< II
->c_str() << _T("\n");
249 for (std::vector
<std::wstring
>::iterator II
= entry
.translatorcomments
.begin(); II
!= entry
.translatorcomments
.end(); ++II
)
251 File
<< II
->c_str() << _T("\n");
253 if (!I
->second
.resourceIDs
.empty())
255 File
<< _T("#. Resource IDs: (");
257 std::set
<INT_PTR
>::const_iterator II
= I
->second
.resourceIDs
.begin();
260 while (II
!= I
->second
.resourceIDs
.end())
268 if (I
->second
.flag
.length() > 0)
269 File
<< (I
->second
.flag
.c_str()) << _T("\n");
270 File
<< (_T("msgid \"")) << (I
->first
.c_str()) << _T("\"\n");
271 File
<< (_T("msgstr \"")) << (I
->second
.msgstr
.c_str()) << _T("\"\n\n");
276 _ftprintf(stdout
, _T("File %s saved, containing %d entries\n"), szPath
, nEntries
);
280 void CPOFile::AdjustEOLs(std::wstring
& str
)
283 std::wstring::size_type pos
= 0;
284 for ( ; ; ) // while (true)
286 std::wstring::size_type next
= str
.find(L
"\\r\\n", pos
);
287 result
.append(str
, pos
, next
-pos
);
288 if( next
!= std::string::npos
)
290 result
.append(L
"\\n");
291 pos
= next
+ 4; // 4 = sizeof("\\r\\n")
302 for ( ; ; ) // while (true)
304 std::wstring::size_type next
= str
.find(L
"\\n", pos
);
305 result
.append(str
, pos
, next
-pos
);
306 if( next
!= std::string::npos
)
308 result
.append(L
"\\r\\n");
309 pos
= next
+ 2; // 2 = sizeof("\\n")