fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / l10ntools / source / po.cxx
blobc2acc2e25e8a6d3764a34a61d1360a088e09aaa6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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 */
10 #include <rtl/ustring.hxx>
12 #include <cstring>
13 #include <ctime>
14 #include <cassert>
16 #include <vector>
17 #include <string>
19 #ifdef _MSC_VER
20 #pragma warning (push, 1)
21 #pragma warning (disable: 4245)
22 #endif
23 #include <boost/crc.hpp>
24 #ifdef _MSC_VER
25 #pragma warning (pop)
26 #endif
29 #include "po.hxx"
30 #include "helper.hxx"
32 /** Container of po entry
34 Provide all file operations related to LibreOffice specific
35 po entry and store it's attributes.
37 class GenPoEntry
39 private:
41 OString m_sExtractCom;
42 OString m_sReference;
43 OString m_sMsgCtxt;
44 OString m_sMsgId;
45 OString m_sMsgStr;
46 bool m_bFuzzy;
47 bool m_bNull;
49 public:
51 GenPoEntry();
52 virtual ~GenPoEntry();
53 // Default copy constructor and copy operator work well
55 OString getReference() const { return m_sReference; }
56 OString getMsgCtxt() const { return m_sMsgCtxt; }
57 OString getMsgId() const { return m_sMsgId; }
58 OString getMsgStr() const { return m_sMsgStr; }
59 bool isFuzzy() const { return m_bFuzzy; }
60 bool isNull() const { return m_bNull; }
62 void setExtractCom(const OString& rExtractCom)
64 m_sExtractCom = rExtractCom;
66 void setReference(const OString& rReference)
68 m_sReference = rReference;
70 void setMsgCtxt(const OString& rMsgCtxt)
72 m_sMsgCtxt = rMsgCtxt;
74 void setMsgId(const OString& rMsgId)
76 m_sMsgId = rMsgId;
78 void setMsgStr(const OString& rMsgStr)
80 m_sMsgStr = rMsgStr;
83 void writeToFile(std::ofstream& rOFStream) const;
84 void readFromFile(std::ifstream& rIFStream);
87 namespace
89 // Convert a normal string to msg/po output string
90 static OString lcl_GenMsgString(const OString& rString)
92 if ( rString.isEmpty() )
93 return "\"\"";
95 OString sResult =
96 "\"" +
97 helper::escapeAll(rString,"\n""\t""\r""\\""\"","\\n""\\t""\\r""\\\\""\\\"") +
98 "\"";
99 sal_Int32 nIndex = 0;
100 while((nIndex=sResult.indexOf("\\n",nIndex))!=-1)
102 if( sResult.copy(nIndex-1,3)!="\\\\n" &&
103 nIndex!=sResult.getLength()-3)
105 sResult = sResult.replaceAt(nIndex,2,"\\n\"\n\"");
107 ++nIndex;
110 if ( sResult.indexOf('\n') != -1 )
111 return "\"\"\n" + sResult;
113 return sResult;
116 // Convert msg string to normal form
117 static OString lcl_GenNormString(const OString& rString)
119 return
120 helper::unEscapeAll(
121 rString.copy(1,rString.getLength()-2),
122 "\\n""\\t""\\r""\\\\""\\\"",
123 "\n""\t""\r""\\""\"");
127 GenPoEntry::GenPoEntry()
128 : m_sExtractCom( OString() )
129 , m_sReference( OString() )
130 , m_sMsgCtxt( OString() )
131 , m_sMsgId( OString() )
132 , m_sMsgStr( OString() )
133 , m_bFuzzy( false )
134 , m_bNull( false )
138 GenPoEntry::~GenPoEntry()
142 void GenPoEntry::writeToFile(std::ofstream& rOFStream) const
144 if ( rOFStream.tellp() != std::ofstream::pos_type( 0 ))
145 rOFStream << std::endl;
146 if ( !m_sExtractCom.isEmpty() )
147 rOFStream
148 << "#. "
149 << m_sExtractCom.replaceAll("\n","\n#. ").getStr() << std::endl;
150 if ( !m_sReference.isEmpty() )
151 rOFStream << "#: " << m_sReference.getStr() << std::endl;
152 if ( m_bFuzzy )
153 rOFStream << "#, fuzzy" << std::endl;
154 if ( !m_sMsgCtxt.isEmpty() )
155 rOFStream << "msgctxt "
156 << lcl_GenMsgString(m_sReference+"\n"+m_sMsgCtxt).getStr()
157 << std::endl;
158 rOFStream << "msgid "
159 << lcl_GenMsgString(m_sMsgId).getStr() << std::endl;
160 rOFStream << "msgstr "
161 << lcl_GenMsgString(m_sMsgStr).getStr() << std::endl;
164 void GenPoEntry::readFromFile(std::ifstream& rIFStream)
166 *this = GenPoEntry();
167 OString* pLastMsg = 0;
168 std::string sTemp;
169 getline(rIFStream,sTemp);
170 if( rIFStream.eof() || sTemp.empty() )
172 m_bNull = true;
173 return;
175 while(!rIFStream.eof())
177 OString sLine = OString(sTemp.data(),sTemp.length());
178 if (sLine.startsWith("#. "))
180 if( !m_sExtractCom.isEmpty() )
182 m_sExtractCom += "\n";
184 m_sExtractCom += sLine.copy(3);
186 else if (sLine.startsWith("#: "))
188 m_sReference = sLine.copy(3);
190 else if (sLine.startsWith("#, fuzzy"))
192 m_bFuzzy = true;
194 else if (sLine.startsWith("msgctxt "))
196 m_sMsgCtxt = lcl_GenNormString(sLine.copy(8));
197 pLastMsg = &m_sMsgCtxt;
199 else if (sLine.startsWith("msgid "))
201 m_sMsgId = lcl_GenNormString(sLine.copy(6));
202 pLastMsg = &m_sMsgId;
204 else if (sLine.startsWith("msgstr "))
206 m_sMsgStr = lcl_GenNormString(sLine.copy(7));
207 pLastMsg = &m_sMsgStr;
209 else if (sLine.startsWith("\"") && pLastMsg)
211 if (pLastMsg != &m_sMsgCtxt || sLine != "\"" + m_sReference + "\\n\"")
213 *pLastMsg += lcl_GenNormString(sLine);
216 else
217 break;
218 getline(rIFStream,sTemp);
223 // Class PoEntry
226 PoEntry::PoEntry()
227 : m_pGenPo( 0 )
228 , m_bIsInitialized( false )
232 PoEntry::PoEntry(
233 const OString& rSourceFile, const OString& rResType, const OString& rGroupId,
234 const OString& rLocalId, const OString& rHelpText,
235 const OString& rText, const TYPE eType )
236 : m_pGenPo( 0 )
237 , m_bIsInitialized( false )
239 if( rSourceFile.isEmpty() )
240 throw NOSOURCFILE;
241 else if ( rResType.isEmpty() )
242 throw NORESTYPE;
243 else if ( rGroupId.isEmpty() )
244 throw NOGROUPID;
245 else if ( rText.isEmpty() )
246 throw NOSTRING;
247 else if ( rHelpText.getLength() == 5 )
248 throw WRONGHELPTEXT;
250 m_pGenPo = new GenPoEntry();
251 m_pGenPo->setReference(rSourceFile.copy(rSourceFile.lastIndexOf('/')+1));
253 OString sMsgCtxt =
254 rGroupId + "\n" +
255 (rLocalId.isEmpty() ? OString( "" ) : rLocalId + "\n") +
256 rResType;
257 switch(eType){
258 case TTEXT:
259 sMsgCtxt += ".text"; break;
260 case TQUICKHELPTEXT:
261 sMsgCtxt += ".quickhelptext"; break;
262 case TTITLE:
263 sMsgCtxt += ".title"; break;
264 // Default case is unneeded because the type of eType has only three element
266 m_pGenPo->setMsgCtxt(sMsgCtxt);
267 m_pGenPo->setMsgId(rText);
268 m_pGenPo->setExtractCom(
269 ( !rHelpText.isEmpty() ? rHelpText + "\n" : OString( "" )) +
270 genKeyId( m_pGenPo->getReference() + rGroupId + rLocalId + rResType + rText ) );
271 m_bIsInitialized = true;
274 PoEntry::~PoEntry()
276 delete m_pGenPo;
279 PoEntry::PoEntry( const PoEntry& rPo )
280 : m_pGenPo( rPo.m_pGenPo ? new GenPoEntry( *(rPo.m_pGenPo) ) : 0 )
281 , m_bIsInitialized( rPo.m_bIsInitialized )
285 PoEntry& PoEntry::operator=(const PoEntry& rPo)
287 if( this == &rPo )
289 return *this;
291 if( rPo.m_pGenPo )
293 if( m_pGenPo )
295 *m_pGenPo = *(rPo.m_pGenPo);
297 else
299 m_pGenPo = new GenPoEntry( *(rPo.m_pGenPo) );
302 else
304 delete m_pGenPo;
305 m_pGenPo = 0;
307 m_bIsInitialized = rPo.m_bIsInitialized;
308 return *this;
311 OString PoEntry::getSourceFile() const
313 assert( m_bIsInitialized );
314 return m_pGenPo->getReference();
317 OString PoEntry::getGroupId() const
319 assert( m_bIsInitialized );
320 return m_pGenPo->getMsgCtxt().getToken(0,'\n');
323 OString PoEntry::getLocalId() const
325 assert( m_bIsInitialized );
326 const OString sMsgCtxt = m_pGenPo->getMsgCtxt();
327 if (sMsgCtxt.indexOf('\n')==sMsgCtxt.lastIndexOf('\n'))
328 return OString();
329 else
330 return sMsgCtxt.getToken(1,'\n');
333 OString PoEntry::getResourceType() const
335 assert( m_bIsInitialized );
336 const OString sMsgCtxt = m_pGenPo->getMsgCtxt();
337 if (sMsgCtxt.indexOf('\n')==sMsgCtxt.lastIndexOf('\n'))
338 return sMsgCtxt.getToken(1,'\n').getToken(0,'.');
339 else
340 return sMsgCtxt.getToken(2,'\n').getToken(0,'.');
343 PoEntry::TYPE PoEntry::getType() const
345 assert( m_bIsInitialized );
346 const OString sMsgCtxt = m_pGenPo->getMsgCtxt();
347 const OString sType = sMsgCtxt.copy( sMsgCtxt.lastIndexOf('.') + 1 );
348 assert(
349 (sType == "text" || sType == "quickhelptext" || sType == "title") );
350 if ( sType == "text" )
351 return TTEXT;
352 else if ( sType == "quickhelptext" )
353 return TQUICKHELPTEXT;
354 else
355 return TTITLE;
358 bool PoEntry::isFuzzy() const
360 assert( m_bIsInitialized );
361 return m_pGenPo->isFuzzy();
364 // Get translation string in merge format
365 OString PoEntry::getMsgId() const
367 assert( m_bIsInitialized );
368 return m_pGenPo->getMsgId();
371 // Get translated string in merge format
372 OString PoEntry::getMsgStr() const
374 assert( m_bIsInitialized );
375 return m_pGenPo->getMsgStr();
379 bool PoEntry::IsInSameComp(const PoEntry& rPo1,const PoEntry& rPo2)
381 assert( rPo1.m_bIsInitialized && rPo2.m_bIsInitialized );
382 return ( rPo1.getSourceFile() == rPo2.getSourceFile() &&
383 rPo1.getGroupId() == rPo2.getGroupId() &&
384 rPo1.getLocalId() == rPo2.getLocalId() &&
385 rPo1.getResourceType() == rPo2.getResourceType() );
388 OString PoEntry::genKeyId(const OString& rGenerator)
390 boost::crc_32_type aCRC32;
391 aCRC32.process_bytes(rGenerator.getStr(), rGenerator.getLength());
392 sal_uInt32 nCRC = aCRC32.checksum();
393 // Use simple ASCII characters, exclude I, l, 1 and O, 0 to avoid confusing IDs
394 static const char sSymbols[] =
395 "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789";
396 char sKeyId[6];
397 for( short nKeyInd = 0; nKeyInd < 5; ++nKeyInd )
399 sKeyId[nKeyInd] = sSymbols[(nCRC & 63) % strlen(sSymbols)];
400 nCRC >>= 6;
402 sKeyId[5] = '\0';
403 return OString(sKeyId);
407 // Class PoHeader
410 namespace
412 // Get actual time in "YEAR-MO-DA HO:MI+ZONE" form
413 static OString lcl_GetTime()
415 time_t aNow = time(NULL);
416 struct tm* pNow = localtime(&aNow);
417 char pBuff[50];
418 strftime( pBuff, sizeof pBuff, "%Y-%m-%d %H:%M%z", pNow );
419 return pBuff;
423 PoHeader::PoHeader( const OString& rExtSrc )
424 : m_pGenPo( new GenPoEntry() )
425 , m_bIsInitialized( false )
427 m_pGenPo->setExtractCom("extracted from " + rExtSrc);
428 m_pGenPo->setMsgStr(
429 OString("Project-Id-Version: PACKAGE VERSION\n"
430 "Report-Msgid-Bugs-To: https://bugs.libreoffice.org/enter_bug.cgi?"
431 "product=LibreOffice&bug_status=UNCONFIRMED&component=UI\n"
432 "POT-Creation-Date: ") + lcl_GetTime() +
433 OString("\nPO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
434 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
435 "Language-Team: LANGUAGE <LL@li.org>\n"
436 "MIME-Version: 1.0\n"
437 "Content-Type: text/plain; charset=UTF-8\n"
438 "Content-Transfer-Encoding: 8bit\n"
439 "X-Generator: LibreOffice\n"
440 "X-Accelerator-Marker: ~\n"));
441 m_bIsInitialized = true;
444 PoHeader::~PoHeader()
446 delete m_pGenPo;
450 // Class PoOfstream
453 PoOfstream::PoOfstream()
454 : m_aOutPut()
455 , m_bIsAfterHeader( false )
459 PoOfstream::PoOfstream(const OString& rFileName, OpenMode aMode )
460 : m_aOutPut()
461 , m_bIsAfterHeader( false )
463 open( rFileName, aMode );
466 PoOfstream::~PoOfstream()
468 if( isOpen() )
470 close();
474 void PoOfstream::open(const OString& rFileName, OpenMode aMode )
476 assert( !isOpen() );
477 if( aMode == TRUNC )
479 m_aOutPut.open( rFileName.getStr(),
480 std::ios_base::out | std::ios_base::trunc );
481 m_bIsAfterHeader = false;
483 else if( aMode == APP )
485 m_aOutPut.open( rFileName.getStr(),
486 std::ios_base::out | std::ios_base::app );
487 m_bIsAfterHeader = m_aOutPut.tellp() != std::ofstream::pos_type( 0 );
491 void PoOfstream::close()
493 assert( isOpen() );
494 m_aOutPut.close();
497 void PoOfstream::writeHeader(const PoHeader& rPoHeader)
499 assert( isOpen() && !m_bIsAfterHeader && rPoHeader.m_bIsInitialized );
500 rPoHeader.m_pGenPo->writeToFile( m_aOutPut );
501 m_bIsAfterHeader = true;
504 void PoOfstream::writeEntry( const PoEntry& rPoEntry )
506 assert( isOpen() && m_bIsAfterHeader && rPoEntry.m_bIsInitialized );
507 rPoEntry.m_pGenPo->writeToFile( m_aOutPut );
511 // Class PoIfstream
514 namespace
517 // Check the validity of read entry
518 static bool lcl_CheckInputEntry(const GenPoEntry& rEntry)
520 const OString sMsgCtxt = rEntry.getMsgCtxt();
521 const sal_Int32 nFirstEndLine = sMsgCtxt.indexOf('\n');
522 const sal_Int32 nLastEndLine = sMsgCtxt.lastIndexOf('\n');
523 const sal_Int32 nLastDot = sMsgCtxt.lastIndexOf('.');
524 const OString sType = sMsgCtxt.copy( nLastDot + 1 );
525 return !rEntry.getReference().isEmpty() &&
526 nFirstEndLine > 0 &&
527 (nLastEndLine == nFirstEndLine || nLastEndLine == sMsgCtxt.indexOf('\n',nFirstEndLine+1)) &&
528 nLastDot - nLastEndLine > 1 &&
529 (sType == "text" || sType == "quickhelptext" || sType == "title")&&
530 !rEntry.getMsgId().isEmpty();
535 PoIfstream::PoIfstream()
536 : m_aInPut()
537 , m_bEof( false )
541 PoIfstream::PoIfstream(const OString& rFileName)
542 : m_aInPut()
543 , m_bEof( false )
545 open( rFileName );
548 PoIfstream::~PoIfstream()
550 if( isOpen() )
552 close();
556 void PoIfstream::open( const OString& rFileName )
558 assert( !isOpen() );
559 m_aInPut.open( rFileName.getStr(), std::ios_base::in );
561 // Skip header
562 std::string sTemp;
563 std::getline(m_aInPut,sTemp);
564 while( !sTemp.empty() && !m_aInPut.eof() )
566 std::getline(m_aInPut,sTemp);
568 m_bEof = false;
571 void PoIfstream::close()
573 assert( isOpen() );
574 m_aInPut.close();
577 void PoIfstream::readEntry( PoEntry& rPoEntry )
579 assert( isOpen() && !eof() );
580 GenPoEntry aGenPo;
581 aGenPo.readFromFile( m_aInPut );
582 if( aGenPo.isNull() )
584 m_bEof = true;
585 rPoEntry = PoEntry();
587 else
589 if( lcl_CheckInputEntry(aGenPo) )
591 if( rPoEntry.m_pGenPo )
593 *(rPoEntry.m_pGenPo) = aGenPo;
595 else
597 rPoEntry.m_pGenPo = new GenPoEntry( aGenPo );
599 rPoEntry.m_bIsInitialized = true;
601 else
603 throw INVALIDENTRY;
608 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */