Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : */
9 :
10 : #include <rtl/ustring.hxx>
11 :
12 : #include <cstring>
13 : #include <ctime>
14 : #include <cassert>
15 :
16 : #include <vector>
17 : #include <string>
18 :
19 : #include <boost/crc.hpp>
20 : #include <unicode/regex.h>
21 :
22 : #include "po.hxx"
23 :
24 : #define POESCAPED OString("\\n\\t\\r\\\\\\\"")
25 : #define POUNESCAPED OString("\n\t\r\\\"")
26 :
27 : using namespace U_ICU_NAMESPACE;
28 :
29 : /** Container of po entry
30 :
31 : Provide all file operations related to LibreOffice specific
32 : po entry and store it's attributes.
33 : */
34 0 : class GenPoEntry
35 : {
36 : private:
37 :
38 : OString m_sExtractCom;
39 : OString m_sReference;
40 : OString m_sMsgCtxt;
41 : OString m_sMsgId;
42 : OString m_sMsgStr;
43 : bool m_bFuzzy;
44 : bool m_bNull;
45 :
46 : public:
47 :
48 : GenPoEntry();
49 : virtual ~GenPoEntry();
50 : //Default copy constructor and copy operator work well
51 :
52 0 : virtual OString getExtractCom() const { return m_sExtractCom; }
53 0 : virtual OString getReference() const { return m_sReference; }
54 0 : virtual OString getMsgCtxt() const { return m_sMsgCtxt; }
55 0 : virtual OString getMsgId() const { return m_sMsgId; }
56 0 : virtual OString getMsgStr() const { return m_sMsgStr; }
57 0 : virtual bool isFuzzy() const { return m_bFuzzy; }
58 0 : virtual bool isNull() const { return m_bNull; }
59 :
60 0 : virtual void setExtractCom(const OString& rExtractCom)
61 : {
62 0 : m_sExtractCom = rExtractCom;
63 0 : }
64 0 : virtual void setReference(const OString& rReference)
65 : {
66 0 : m_sReference = rReference;
67 0 : }
68 0 : virtual void setMsgCtxt(const OString& rMsgCtxt)
69 : {
70 0 : m_sMsgCtxt = rMsgCtxt;
71 0 : }
72 0 : virtual void setMsgId(const OString& rMsgId)
73 : {
74 0 : m_sMsgId = rMsgId;
75 0 : }
76 0 : virtual void setMsgStr(const OString& rMsgStr)
77 : {
78 0 : m_sMsgStr = rMsgStr;
79 0 : }
80 0 : virtual void setFuzzy(const bool bFuzzy)
81 : {
82 0 : m_bFuzzy = bFuzzy;
83 0 : }
84 :
85 : virtual void writeToFile(std::ofstream& rOFStream) const;
86 : virtual void readFromFile(std::ifstream& rIFStream);
87 : };
88 :
89 : namespace
90 : {
91 : //Escape text
92 0 : static OString lcl_EscapeText(const OString& rText,
93 : const OString& rUnEscaped= POUNESCAPED,
94 : const OString& rEscaped = POESCAPED)
95 : {
96 : assert( rEscaped.getLength() == 2*rUnEscaped.getLength() );
97 0 : OString sResult = rText;
98 0 : int nCount = 0;
99 0 : for(sal_Int32 nIndex=0; nIndex<rText.getLength(); ++nIndex)
100 : {
101 0 : sal_Int32 nActChar = rUnEscaped.indexOf(rText[nIndex]);
102 0 : if(nActChar!=-1)
103 : sResult = sResult.replaceAt((nIndex)+(nCount++),1,
104 0 : rEscaped.copy(2*nActChar,2));
105 : }
106 0 : return sResult;
107 : }
108 :
109 : //Unescape text
110 0 : static OString lcl_UnEscapeText(const OString& rText,
111 : const OString& rEscaped = POESCAPED,
112 : const OString& rUnEscaped = POUNESCAPED)
113 : {
114 : assert( rEscaped.getLength() == 2*rUnEscaped.getLength() );
115 0 : OString sResult = rText;
116 0 : int nCount = 0;
117 0 : for(sal_Int32 nIndex=0; nIndex<rText.getLength()-1; ++nIndex)
118 : {
119 0 : sal_Int32 nActChar = rEscaped.indexOf(rText.copy(nIndex,2));
120 0 : if(nActChar % 2 == 0)
121 : sResult = sResult.replaceAt((nIndex++)-(nCount++),2,
122 0 : rUnEscaped.copy(nActChar/2,1));
123 : }
124 0 : return sResult;
125 : }
126 :
127 : //Convert a normal string to msg/po output string
128 0 : static OString lcl_GenMsgString(const OString& rString)
129 : {
130 0 : if ( rString.isEmpty() )
131 0 : return "\"\"";
132 :
133 0 : OString sResult = "\"" + lcl_EscapeText(rString) + "\"";
134 0 : sal_Int32 nIndex = 0;
135 0 : while((nIndex=sResult.indexOf("\\n",nIndex))!=-1)
136 : {
137 0 : if( sResult.copy(nIndex-1,3)!="\\\\n" &&
138 0 : nIndex!=sResult.getLength()-3)
139 : {
140 0 : sResult = sResult.replaceAt(nIndex,2,"\\n\"\n\"");
141 : }
142 0 : ++nIndex;
143 : }
144 :
145 0 : if ( sResult.indexOf('\n') != -1 )
146 0 : return "\"\"\n" + sResult;
147 :
148 0 : return sResult;
149 : }
150 :
151 : //Convert msg string to normal form
152 0 : static OString lcl_GenNormString(const OString& rString)
153 : {
154 0 : return lcl_UnEscapeText(rString.copy(1,rString.getLength()-2));
155 : }
156 : }
157 :
158 : //Default constructor
159 0 : GenPoEntry::GenPoEntry()
160 : : m_sExtractCom( OString() )
161 : , m_sReference( OString() )
162 : , m_sMsgCtxt( OString() )
163 : , m_sMsgId( OString() )
164 : , m_sMsgStr( OString() )
165 : , m_bFuzzy( false )
166 0 : , m_bNull( false )
167 : {
168 0 : }
169 :
170 : //Destructor
171 0 : GenPoEntry::~GenPoEntry()
172 : {
173 0 : }
174 :
175 : //Write to file
176 0 : void GenPoEntry::writeToFile(std::ofstream& rOFStream) const
177 : {
178 0 : if ( rOFStream.tellp() != std::ofstream::pos_type( 0 ))
179 0 : rOFStream << std::endl;
180 0 : if ( !m_sExtractCom.isEmpty() )
181 : rOFStream
182 0 : << "#. "
183 0 : << m_sExtractCom.replaceAll("\n","\n#. ").getStr() << std::endl;
184 0 : if ( !m_sReference.isEmpty() )
185 0 : rOFStream << "#: " << m_sReference.getStr() << std::endl;
186 0 : if ( m_bFuzzy )
187 0 : rOFStream << "#, fuzzy" << std::endl;
188 0 : if ( !m_sMsgCtxt.isEmpty() )
189 0 : rOFStream << "msgctxt "
190 0 : << lcl_GenMsgString(m_sReference+"\n"+m_sMsgCtxt).getStr()
191 0 : << std::endl;
192 0 : rOFStream << "msgid "
193 0 : << lcl_GenMsgString(m_sMsgId).getStr() << std::endl;
194 0 : rOFStream << "msgstr "
195 0 : << lcl_GenMsgString(m_sMsgStr).getStr() << std::endl;
196 0 : }
197 :
198 : //Read from file
199 0 : void GenPoEntry::readFromFile(std::ifstream& rIFStream)
200 : {
201 0 : *this = GenPoEntry();
202 0 : if( rIFStream.eof() )
203 : {
204 0 : m_bNull = true;
205 0 : return;
206 : }
207 0 : OString* pLastMsg = 0;
208 0 : std::string sTemp;
209 0 : getline(rIFStream,sTemp);
210 0 : while(!rIFStream.eof())
211 : {
212 0 : OString sLine = OString(sTemp.data(),sTemp.length());
213 0 : if (sLine.startsWith("#. "))
214 : {
215 0 : if( !m_sExtractCom.isEmpty() )
216 : {
217 0 : m_sExtractCom += "\n";
218 : }
219 0 : m_sExtractCom += sLine.copy(3);
220 : }
221 0 : else if (sLine.startsWith("#: "))
222 : {
223 0 : m_sReference = sLine.copy(3);
224 : }
225 0 : else if (sLine.startsWith("#, fuzzy"))
226 : {
227 0 : m_bFuzzy = true;
228 : }
229 0 : else if (sLine.startsWith("msgctxt "))
230 : {
231 0 : m_sMsgCtxt = lcl_GenNormString(sLine.copy(8));
232 0 : pLastMsg = &m_sMsgCtxt;
233 : }
234 0 : else if (sLine.startsWith("msgid "))
235 : {
236 0 : m_sMsgId = lcl_GenNormString(sLine.copy(6));
237 0 : pLastMsg = &m_sMsgId;
238 : }
239 0 : else if (sLine.startsWith("msgstr "))
240 : {
241 0 : m_sMsgStr = lcl_GenNormString(sLine.copy(7));
242 0 : pLastMsg = &m_sMsgStr;
243 : }
244 0 : else if (sLine.startsWith("\"") && pLastMsg)
245 : {
246 0 : if (pLastMsg != &m_sMsgCtxt || sLine != "\"" + m_sReference + "\\n\"")
247 : {
248 0 : *pLastMsg += lcl_GenNormString(sLine);
249 : }
250 : }
251 : else
252 : break;
253 0 : getline(rIFStream,sTemp);
254 0 : }
255 : }
256 :
257 : //Class PoEntry
258 :
259 : namespace
260 : {
261 : //Generate KeyId
262 0 : static OString lcl_GenKeyId(const OString& rGenerator)
263 : {
264 0 : boost::crc_32_type aCRC32;
265 0 : aCRC32.process_bytes(rGenerator.getStr(), rGenerator.getLength());
266 0 : sal_uInt32 nCRC = aCRC32.checksum();
267 : //Use all readable ASCII character exclude xml special tags: ",',&,<,>
268 0 : const OString sSymbols = "!#$%()*+,-./0123456789:;=?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
269 : char sKeyId[5];
270 0 : for( short nKeyInd = 0; nKeyInd < 4; ++nKeyInd )
271 : {
272 0 : sKeyId[nKeyInd] = sSymbols[(nCRC & 255) % 89];
273 0 : nCRC >>= 8;
274 : }
275 0 : sKeyId[4] = '\0';
276 0 : return OString(sKeyId);
277 : }
278 :
279 : //Split string at the delimiter character
280 0 : static void lcl_SplitAt(const OString& rSource, const sal_Char nDelimiter,
281 : std::vector<OString>& o_vParts)
282 : {
283 0 : o_vParts.resize( 0 );
284 0 : sal_Int32 nActIndex = 0;
285 0 : sal_Int32 nLastSplit = 0;
286 0 : while( nActIndex < rSource.getLength() )
287 : {
288 0 : if ( rSource[nActIndex] == nDelimiter )
289 : {
290 : o_vParts.push_back(
291 0 : rSource.copy(nLastSplit,nActIndex-nLastSplit));
292 0 : nLastSplit = nActIndex+1;
293 : }
294 0 : ++nActIndex;
295 : }
296 0 : o_vParts.push_back(rSource.copy(nLastSplit));
297 0 : }
298 :
299 : //Unescape sdf string
300 0 : static OString lcl_UnEscapeSDFText(
301 : const OString& rText,const bool bHelpText = false )
302 : {
303 0 : if ( bHelpText )
304 0 : return lcl_UnEscapeText(rText,"\\<\\>\\\"\\\\","<>\"\\");
305 : else
306 0 : return lcl_UnEscapeText(rText,"\\n\\t\\r","\n\t\r");
307 : }
308 :
309 : //Find all special tag in a string using a regular expression
310 0 : static void lcl_FindAllTag(
311 : const OString& rText,std::vector<OString>& o_vFoundTags )
312 : {
313 :
314 0 : UErrorCode nIcuErr = U_ZERO_ERROR;
315 0 : sal_uInt32 nSearchFlags = UREGEX_DOTALL | UREGEX_CASE_INSENSITIVE;
316 0 : OUString sLocaleText( OStringToOUString(rText,RTL_TEXTENCODING_UTF8) );
317 0 : OUString sPattern("<[/]\?\?[a-z_-]+?(?:| +[a-z]+?=\".*?\") *[/]\?\?>");
318 : UnicodeString sSearchPat(
319 : reinterpret_cast<const UChar*>(
320 0 : sPattern.getStr()), sPattern.getLength() );
321 : UnicodeString sSource(
322 : reinterpret_cast<const UChar*>(
323 0 : sLocaleText.getStr()), sLocaleText.getLength() );
324 :
325 0 : RegexMatcher aRegexMatcher( sSearchPat, nSearchFlags, nIcuErr );
326 0 : aRegexMatcher.reset( sSource );
327 0 : int64_t nStartPos = 0;
328 0 : while( aRegexMatcher.find(nStartPos, nIcuErr) &&
329 : nIcuErr == U_ZERO_ERROR )
330 : {
331 : UnicodeString sMatch =
332 0 : aRegexMatcher.group(nIcuErr);
333 : o_vFoundTags.push_back(
334 : OUStringToOString(
335 : OUString(
336 : reinterpret_cast<const sal_Unicode*>(
337 0 : sMatch.getBuffer()),sMatch.length()),
338 0 : RTL_TEXTENCODING_UTF8));
339 0 : nStartPos = aRegexMatcher.start(nIcuErr)+1;
340 0 : }
341 0 : }
342 :
343 : //Escape special tags
344 0 : static OString lcl_EscapeTags( const OString& rText )
345 : {
346 : typedef std::vector<OString> StrVec_t;
347 : const OString vInitializer[] = {
348 : "ahelp", "link", "item", "emph", "defaultinline",
349 : "switchinline", "caseinline", "variable",
350 0 : "bookmark_value", "image", "embedvar", "alt" };
351 : const StrVec_t vTagsForEscape( vInitializer,
352 0 : vInitializer + sizeof(vInitializer) / sizeof(vInitializer[0]) );
353 0 : StrVec_t vFoundTags;
354 0 : lcl_FindAllTag(rText,vFoundTags);
355 0 : OString sResult = rText;
356 0 : for(StrVec_t::const_iterator pFound = vFoundTags.begin();
357 0 : pFound != vFoundTags.end(); ++pFound)
358 : {
359 0 : bool bEscapeThis = false;
360 0 : for(StrVec_t::const_iterator pEscape = vTagsForEscape.begin();
361 0 : pEscape != vTagsForEscape.end(); ++pEscape)
362 : {
363 0 : if (pFound->startsWith("<" + *pEscape) ||
364 0 : *pFound == "</" + *pEscape + ">")
365 : {
366 0 : bEscapeThis = true;
367 0 : break;
368 : }
369 : }
370 0 : if( bEscapeThis || *pFound=="<br/>" ||
371 0 : *pFound =="<help-id-missing/>")
372 : {
373 : OString sToReplace = "\\<" +
374 0 : pFound->copy(1,pFound->getLength()-2).
375 0 : replaceAll("\"","\\\"") + "\\>";
376 0 : sResult = sResult.replaceAll(*pFound, sToReplace);
377 : }
378 : }
379 0 : return sResult;
380 : }
381 :
382 : //Escape to get sdf/merge string
383 0 : static OString lcl_EscapeSDFText(
384 : const OString& rText,const bool bHelpText = false )
385 : {
386 0 : if ( bHelpText )
387 0 : return lcl_EscapeTags(rText.replaceAll("\\","\\\\"));
388 : else
389 0 : return lcl_EscapeText(rText,"\n\t\r","\\n\\t\\r");
390 : }
391 : }
392 :
393 : //Default constructor
394 0 : PoEntry::PoEntry()
395 : : m_pGenPo( 0 )
396 0 : , m_bIsInitialized( false )
397 : {
398 0 : }
399 :
400 : //Construct PoEntry from sdfline
401 0 : PoEntry::PoEntry(const OString& rSDFLine, const TYPE eType)
402 : : m_pGenPo( 0 )
403 0 : , m_bIsInitialized( false )
404 : {
405 0 : std::vector<OString> vParts;
406 0 : lcl_SplitAt(rSDFLine,'\t',vParts);
407 0 : if( vParts.size()!=15 ||
408 0 : vParts[SOURCEFILE].isEmpty() ||
409 0 : vParts[GROUPID].isEmpty() ||
410 0 : vParts[RESOURCETYPE].isEmpty() ||
411 0 : vParts[eType].isEmpty() ||
412 0 : vParts[HELPTEXT].getLength() == 4 )
413 : {
414 0 : throw INVALIDSDFLINE;
415 : }
416 :
417 0 : m_pGenPo = new GenPoEntry();
418 0 : m_pGenPo->setReference(vParts[SOURCEFILE].
419 0 : copy(vParts[SOURCEFILE].lastIndexOf("\\")+1));
420 :
421 : OString sMsgCtxt =
422 0 : vParts[GROUPID] + "\n" +
423 0 : (vParts[LOCALID].isEmpty() ? OString( "" ) : vParts[LOCALID] + "\n") +
424 0 : vParts[RESOURCETYPE];
425 0 : switch(eType){
426 : case TTEXT:
427 0 : sMsgCtxt += ".text"; break;
428 : case TQUICKHELPTEXT:
429 0 : sMsgCtxt += ".quickhelptext"; break;
430 : case TTITLE:
431 0 : sMsgCtxt += ".title"; break;
432 : /*Default case is unneeded because the type of eType has
433 : only three element*/
434 : }
435 : m_pGenPo->setExtractCom(
436 0 : ( !vParts[HELPTEXT].isEmpty() ? vParts[HELPTEXT] + "\n" : OString( "" )) +
437 : lcl_GenKeyId(
438 0 : vParts[SOURCEFILE] + sMsgCtxt + vParts[eType] ) );
439 0 : m_pGenPo->setMsgCtxt(sMsgCtxt);
440 : m_pGenPo->setMsgId(
441 : lcl_UnEscapeSDFText(
442 0 : vParts[eType],vParts[SOURCEFILE].endsWith(".xhp")));
443 0 : m_bIsInitialized = true;
444 0 : }
445 :
446 : //Destructor
447 0 : PoEntry::~PoEntry()
448 : {
449 0 : delete m_pGenPo;
450 0 : }
451 :
452 : //Copy constructor
453 0 : PoEntry::PoEntry( const PoEntry& rPo )
454 0 : : m_pGenPo( rPo.m_pGenPo ? new GenPoEntry( *(rPo.m_pGenPo) ) : 0 )
455 0 : , m_bIsInitialized( rPo.m_bIsInitialized )
456 : {
457 0 : }
458 :
459 : //Copy operator
460 0 : PoEntry& PoEntry::operator=(const PoEntry& rPo)
461 : {
462 0 : if( this == &rPo )
463 : {
464 0 : return *this;
465 : }
466 0 : if( rPo.m_pGenPo )
467 : {
468 0 : if( m_pGenPo )
469 : {
470 0 : *m_pGenPo = *(rPo.m_pGenPo);
471 : }
472 : else
473 : {
474 0 : m_pGenPo = new GenPoEntry( *(rPo.m_pGenPo) );
475 : }
476 : }
477 : else
478 : {
479 0 : delete m_pGenPo;
480 0 : m_pGenPo = 0;
481 : }
482 0 : m_bIsInitialized = rPo.m_bIsInitialized;
483 0 : return *this;
484 : }
485 :
486 : //Get name of file from which entry is extracted
487 0 : OString PoEntry::getSourceFile() const
488 : {
489 : assert( m_bIsInitialized );
490 0 : return m_pGenPo->getReference();
491 : }
492 :
493 : //Get groupid
494 0 : OString PoEntry::getGroupId() const
495 : {
496 : assert( m_bIsInitialized );
497 0 : return m_pGenPo->getMsgCtxt().getToken(0,'\n');
498 : }
499 :
500 : //Get localid
501 0 : OString PoEntry::getLocalId() const
502 : {
503 : assert( m_bIsInitialized );
504 0 : const OString sMsgCtxt = m_pGenPo->getMsgCtxt();
505 0 : if (sMsgCtxt.indexOf('\n')==sMsgCtxt.lastIndexOf('\n'))
506 0 : return OString();
507 : else
508 0 : return sMsgCtxt.getToken(1,'\n');
509 : }
510 :
511 : //Get the type of component from which entry is extracted
512 0 : OString PoEntry::getResourceType() const
513 : {
514 : assert( m_bIsInitialized );
515 0 : const OString sMsgCtxt = m_pGenPo->getMsgCtxt();
516 0 : if (sMsgCtxt.indexOf('\n')==sMsgCtxt.lastIndexOf('\n'))
517 0 : return sMsgCtxt.getToken(1,'\n').getToken(0,'.');
518 : else
519 0 : return sMsgCtxt.getToken(2,'\n').getToken(0,'.');
520 : }
521 :
522 : //Get the type of entry
523 0 : PoEntry::TYPE PoEntry::getType() const
524 : {
525 : assert( m_bIsInitialized );
526 0 : const OString sMsgCtxt = m_pGenPo->getMsgCtxt();
527 0 : const OString sType = sMsgCtxt.copy( sMsgCtxt.lastIndexOf('.') + 1 );
528 : assert(
529 : (sType == "text" || sType == "quickhelptext" || sType == "title") );
530 0 : if ( sType == "text" )
531 0 : return TTEXT;
532 0 : else if ( sType == "quickhelptext" )
533 0 : return TQUICKHELPTEXT;
534 : else
535 0 : return TTITLE;
536 : }
537 :
538 : //Check wheather entry is fuzzy
539 0 : bool PoEntry::isFuzzy() const
540 : {
541 : assert( m_bIsInitialized );
542 0 : return m_pGenPo->isFuzzy();
543 : }
544 :
545 : //Get keyid
546 0 : OString PoEntry::getKeyId() const
547 : {
548 : assert( m_bIsInitialized );
549 0 : const OString sExtractCom = m_pGenPo->getExtractCom();
550 0 : if( sExtractCom.indexOf("\n") == -1 )
551 : {
552 0 : return sExtractCom;
553 : }
554 : else
555 : {
556 0 : return sExtractCom.getToken(1,'\n');
557 0 : }
558 : }
559 :
560 :
561 : //Get translation string in sdf/merge format
562 0 : OString PoEntry::getMsgId() const
563 : {
564 : assert( m_bIsInitialized );
565 : return
566 : lcl_EscapeSDFText(
567 0 : m_pGenPo->getMsgId(), getSourceFile().endsWith(".xhp") );
568 : }
569 :
570 : //Get translated string in sdf/merge format
571 0 : OString PoEntry::getMsgStr() const
572 : {
573 : assert( m_bIsInitialized );
574 : return
575 : lcl_EscapeSDFText(
576 0 : m_pGenPo->getMsgStr(), getSourceFile().endsWith(".xhp") );
577 :
578 : }
579 :
580 : //Set translated string when input is in sdf format
581 0 : void PoEntry::setMsgStr(const OString& rMsgStr)
582 : {
583 : assert( m_bIsInitialized );
584 : m_pGenPo->setMsgStr(
585 : lcl_UnEscapeSDFText(
586 0 : rMsgStr,getSourceFile().endsWith(".xhp")));
587 0 : }
588 :
589 : //Set fuzzy flag
590 0 : void PoEntry::setFuzzy(const bool bFuzzy)
591 : {
592 : assert( m_bIsInitialized );
593 0 : m_pGenPo->setFuzzy(bFuzzy);
594 0 : }
595 :
596 : //Check whether po-s belong to the same localization component
597 0 : bool PoEntry::IsInSameComp(const PoEntry& rPo1,const PoEntry& rPo2)
598 : {
599 : assert( rPo1.m_bIsInitialized && rPo2.m_bIsInitialized );
600 0 : return ( rPo1.getSourceFile() == rPo2.getSourceFile() &&
601 0 : rPo1.getGroupId() == rPo2.getGroupId() &&
602 0 : rPo1.getLocalId() == rPo2.getLocalId() &&
603 0 : rPo1.getResourceType() == rPo2.getResourceType() );
604 : }
605 :
606 : //Class PoHeader
607 :
608 : namespace
609 : {
610 : //Get actual time in "YEAR-MO-DA HO:MI+ZONE" form
611 0 : static OString lcl_GetTime()
612 : {
613 0 : time_t aNow = time(NULL);
614 0 : struct tm* pNow = localtime(&aNow);
615 : char pBuff[50];
616 0 : strftime( pBuff, sizeof pBuff, "%Y-%m-%d %H:%M%z", pNow );
617 0 : return pBuff;
618 : }
619 :
620 0 : static OString lcl_ReplaceAttribute(
621 : const OString& rSource, const OString& rOld, const OString& rNew )
622 : {
623 : const sal_Int32 nFirstIndex =
624 0 : rSource.indexOf( rOld ) + rOld.getLength()+2;
625 : const sal_Int32 nCount =
626 0 : rSource.indexOf( "\n", nFirstIndex ) - nFirstIndex;
627 0 : return rSource.replaceFirst( rSource.copy(nFirstIndex, nCount), rNew );
628 : }
629 : }
630 :
631 : //Default Constructor
632 0 : PoHeader::PoHeader()
633 : : m_pGenPo( 0 )
634 0 : , m_bIsInitialized( false )
635 : {
636 0 : }
637 :
638 : //Template Constructor
639 0 : PoHeader::PoHeader( const OString& rExtSrc )
640 : : m_pGenPo( new GenPoEntry() )
641 0 : , m_bIsInitialized( false )
642 : {
643 0 : m_pGenPo->setExtractCom("extracted from " + rExtSrc);
644 : m_pGenPo->setMsgStr(
645 : OString("Project-Id-Version: PACKAGE VERSION\n"
646 : "Report-Msgid-Bugs-To: https://bugs.freedesktop.org/enter_bug.cgi?"
647 : "product=LibreOffice&bug_status=UNCONFIRMED&component=UI\n"
648 0 : "POT-Creation-Date: ") + lcl_GetTime() +
649 : OString("\nPO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
650 : "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
651 : "Language-Team: LANGUAGE <LL@li.org>\n"
652 : "MIME-Version: 1.0\n"
653 : "Content-Type: text/plain; charset=UTF-8\n"
654 : "Content-Transfer-Encoding: 8bit\n"
655 : "X-Generator: LibreOffice\n"
656 0 : "X-Accelerator-Marker: ~\n"));
657 0 : m_bIsInitialized = true;
658 0 : }
659 :
660 :
661 : //Constructor for old headers to renew po files
662 0 : PoHeader::PoHeader( std::ifstream& rOldPo )
663 : : m_pGenPo( new GenPoEntry() )
664 0 : , m_bIsInitialized( false )
665 : {
666 : assert( rOldPo.is_open() );
667 0 : m_pGenPo->readFromFile( rOldPo );
668 :
669 0 : const OString sExtractCom = m_pGenPo->getExtractCom();
670 : m_pGenPo->setExtractCom(
671 0 : sExtractCom.copy( 0, sExtractCom.getLength() - 3 ) );
672 :
673 0 : OString sMsgStr = m_pGenPo->getMsgStr();
674 : sMsgStr =
675 : lcl_ReplaceAttribute( sMsgStr, "Report-Msgid-Bugs-To",
676 : "https://bugs.freedesktop.org/enter_bug.cgi?product="
677 0 : "LibreOffice&bug_status=UNCONFIRMED&component=UI" );
678 : sMsgStr =
679 0 : lcl_ReplaceAttribute( sMsgStr, "X-Generator", "LibreOffice" );
680 : sMsgStr =
681 0 : lcl_ReplaceAttribute( sMsgStr, "X-Accelerator-Marker", "~" );
682 0 : m_pGenPo->setMsgStr( sMsgStr );
683 0 : m_bIsInitialized = true;
684 0 : }
685 :
686 0 : PoHeader::~PoHeader()
687 : {
688 0 : delete m_pGenPo;
689 0 : }
690 :
691 : //Class PoOfstream
692 :
693 0 : PoOfstream::PoOfstream()
694 : : m_aOutPut()
695 0 : , m_bIsAfterHeader( false )
696 : {
697 0 : }
698 :
699 0 : PoOfstream::~PoOfstream()
700 : {
701 0 : if( isOpen() )
702 : {
703 0 : close();
704 : }
705 0 : }
706 :
707 0 : void PoOfstream::open(const OString& rFileName)
708 : {
709 : assert( !isOpen() );
710 : m_aOutPut.open( rFileName.getStr(),
711 0 : std::ios_base::out | std::ios_base::trunc );
712 0 : m_bIsAfterHeader = false;
713 0 : }
714 :
715 0 : void PoOfstream::close()
716 : {
717 : assert( isOpen() );
718 0 : m_aOutPut.close();
719 0 : }
720 :
721 0 : void PoOfstream::writeHeader(const PoHeader& rPoHeader)
722 : {
723 : assert( isOpen() && !m_bIsAfterHeader && rPoHeader.m_bIsInitialized );
724 0 : rPoHeader.m_pGenPo->writeToFile( m_aOutPut );
725 0 : m_bIsAfterHeader = true;
726 0 : }
727 :
728 0 : void PoOfstream::writeEntry( const PoEntry& rPoEntry )
729 : {
730 : assert( isOpen() && m_bIsAfterHeader && rPoEntry.m_bIsInitialized );
731 0 : rPoEntry.m_pGenPo->writeToFile( m_aOutPut );
732 0 : }
733 :
734 : //Class PoIfstream
735 :
736 0 : PoIfstream::PoIfstream()
737 : : m_aInPut()
738 0 : , m_bEof( false )
739 : {
740 0 : }
741 :
742 0 : PoIfstream::~PoIfstream()
743 : {
744 0 : if( isOpen() )
745 : {
746 0 : close();
747 : }
748 0 : }
749 :
750 0 : void PoIfstream::open( const OString& rFileName )
751 : {
752 : assert( !isOpen() );
753 0 : m_aInPut.open( rFileName.getStr(), std::ios_base::in );
754 :
755 : //Skip header
756 0 : std::string sTemp;
757 0 : std::getline(m_aInPut,sTemp);
758 0 : while( !sTemp.empty() && !m_aInPut.eof() )
759 : {
760 0 : std::getline(m_aInPut,sTemp);
761 : }
762 0 : m_bEof = false;
763 0 : }
764 :
765 0 : void PoIfstream::close()
766 : {
767 : assert( isOpen() );
768 0 : m_aInPut.close();
769 0 : }
770 :
771 0 : void PoIfstream::readEntry( PoEntry& rPoEntry )
772 : {
773 : assert( isOpen() && !eof() );
774 0 : GenPoEntry aGenPo;
775 0 : aGenPo.readFromFile( m_aInPut );
776 0 : if( aGenPo.isNull() )
777 : {
778 0 : m_bEof = true;
779 0 : rPoEntry = PoEntry();
780 : }
781 : else
782 : {
783 0 : const OString sMsgCtxt = aGenPo.getMsgCtxt();
784 0 : const sal_Int32 nFirstEndLine = sMsgCtxt.indexOf('\n');
785 0 : const sal_Int32 nLastEndLine = sMsgCtxt.lastIndexOf('\n');
786 0 : const sal_Int32 nLastDot = sMsgCtxt.lastIndexOf('.');
787 0 : const OString sType = sMsgCtxt.copy( nLastDot + 1 );
788 0 : if( !aGenPo.getReference().isEmpty() &&
789 : nFirstEndLine > 0 &&
790 : (nLastEndLine == nFirstEndLine ||
791 0 : nLastEndLine == sMsgCtxt.indexOf('\n',nFirstEndLine+1)) &&
792 : nLastDot - nLastEndLine > 1 &&
793 0 : (sType == "text" || sType == "quickhelptext" || sType == "title")&&
794 0 : !aGenPo.getMsgId().isEmpty() )
795 : {
796 : //Generate keyid if po file not includes it
797 0 : const OString sExtractCom = aGenPo.getExtractCom();
798 0 : if( sExtractCom.isEmpty() ||
799 0 : ( sExtractCom.getLength() != 4 &&
800 0 : sExtractCom.indexOf("\n") == -1 ) )
801 : {
802 : aGenPo.setExtractCom(
803 0 : ( !sExtractCom.isEmpty() ? sExtractCom + "\n" : OString( "" )) +
804 : lcl_GenKeyId(
805 0 : aGenPo.getReference() + sMsgCtxt +
806 0 : aGenPo.getMsgId() ) );
807 : }
808 0 : if( rPoEntry.m_pGenPo )
809 : {
810 0 : *(rPoEntry.m_pGenPo) = aGenPo;
811 : }
812 : else
813 : {
814 0 : rPoEntry.m_pGenPo = new GenPoEntry( aGenPo );
815 : }
816 0 : rPoEntry.m_bIsInitialized = true;
817 : }
818 : else
819 : {
820 0 : throw INVALIDENTRY;
821 0 : }
822 0 : }
823 0 : }
824 :
825 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|