GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/utils/translation/poparser.cpp Lines: 100 108 92.6 %
Date: 2021-03-17 Branches: 78 120 65.0 %

Line Branch Exec Source
1
/*
2
 *  The ManaPlus Client
3
 *  Copyright (C) 2012-2019  The ManaPlus Developers
4
 *  Copyright (C) 2019-2021  Andrei Karas
5
 *
6
 *  This file is part of The ManaPlus Client.
7
 *
8
 *  This program is free software; you can redistribute it and/or modify
9
 *  it under the terms of the GNU General Public License as published by
10
 *  the Free Software Foundation; either version 2 of the License, or
11
 *  any later version.
12
 *
13
 *  This program is distributed in the hope that it will be useful,
14
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 *  GNU General Public License for more details.
17
 *
18
 *  You should have received a copy of the GNU General Public License
19
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
20
 */
21
22
#include "utils/translation/poparser.h"
23
24
#include "fs/virtfs/fs.h"
25
26
#include "utils/stringutils.h"
27
28
#include "utils/translation/podict.h"
29
30
#include "logger.h"
31
32
#include "debug.h"
33
34
3
PoParser::PoParser() :
35
    mLang(),
36
    mFile(),
37
    mLine(),
38
    mMsgId(),
39
    mMsgStr(),
40
    mDict(nullptr),
41
    mReadingId(false),
42
    mReadingStr(false),
43
15
    mSkipId(false)
44
{
45
3
}
46
47
3
void PoParser::openFile(const std::string &name)
48
{
49
    int size;
50
6
    const char *buf = VirtFs::loadFile(getFileName(name), size);
51
52
3
    if (buf != nullptr)
53
    {
54
12
        mFile.str(std::string(buf, size));
55
2
        delete [] buf;
56
    }
57
    else
58
    {
59
1
        mFile.clear();
60
    }
61
3
}
62
63
3
PoDict *PoParser::load(const std::string &restrict lang,
64
                       const std::string &restrict fileName,
65
                       PoDict *restrict const dict)
66
{
67
6
    logger->log("loading lang: %s, file: %s", lang.c_str(), fileName.c_str());
68
69
3
    setLang(lang);
70
3
    if (dict == nullptr)
71
3
        mDict = getDict();
72
    else
73
        mDict = dict;
74
75
3
    if (fileName.empty())
76
        openFile(mLang);
77
    else
78
3
        openFile(fileName);
79
80
6
    mMsgId.clear();
81
3
    mMsgStr.clear();
82
83
    // cycle by msgid+msgstr
84
3635
    while (readLine())
85
    {
86
        // reading msgid
87
16788
        while (readMsgId())
88
        {
89
13156
            if (!readLine())
90
                break;
91
        }
92
93
7264
        if (!mMsgId.empty())
94
        {
95
            // if we got msgid then reading msgstr
96
7338
            while (readMsgStr())
97
            {
98
3710
                if (!readLine())
99
                    break;
100
            }
101
        }
102
103

10894
        if (!mMsgId.empty() && !mMsgStr.empty())
104
        {
105
//            logger->log("add key: " + mMsgId);
106
//            logger->log("add value: " + mMsgStr);
107
108
3572
            convertStr(mMsgId);
109
3572
            convertStr(mMsgStr);
110
            // store key and value
111
3572
            mDict->set(mMsgId, mMsgStr);
112
        }
113
114
7264
        mMsgId.clear();
115
3632
        mMsgStr.clear();
116
    }
117
118
3
    return mDict;
119
}
120
121
20501
bool PoParser::readLine()
122
{
123
    char line[1001];
124
41002
    if (!mFile.getline(line, 1000))
125
        return false;
126
    // skip plurals msgid if present
127

143472
    if (strStartWith(line, "msgid_plural \""))
128
    {
129
12
        if (!mFile.getline(line, 1000))
130
            return false;
131
    }
132
20496
    mLine = line;
133
    return true;
134
}
135
136
16788
bool PoParser::readMsgId()
137
{
138
    // if we reading msgstr then stop here
139
16788
    if (mReadingStr)
140
        return false;
141
142
50364
    const std::string msgId1("msgid \"");
143
144
    // check if in reading process
145
16788
    if (mReadingId)
146
    {
147
        // if we get empty line in file then stop reading
148
7808
        if (mLine.empty())
149
        {
150
            mReadingId = false;
151
            return false;
152
        }
153
3904
        else if (checkLine())
154
        {
155
            // reading text from: "text"
156
816
            mMsgId.append(mLine.substr(1, mLine.size() - 2));
157
544
            mLine.clear();
158
272
            return true;
159
        }
160
        // stop reading in other case
161
3632
        mReadingId = false;
162
3632
        return false;
163
    }
164
165
    // check line start from msgid "
166

12884
    if (strStartWith(mLine, msgId1))
167
    {
168
3634
        if (!mSkipId)
169
        {   // translation not fuzzed and can be processed
170
3632
            mReadingId = true;
171
3632
            const size_t msgId1Size = msgId1.size();
172
            // reading text from: msgid "text"
173
7264
            mMsgId.append(mLine.substr(msgId1Size,
174
10896
                mLine.size() - 1 - msgId1Size));
175
        }
176
        else
177
        {   // skipped fuzzed translation. reset skip flag
178
2
            mSkipId = false;
179
        }
180
7268
        mLine.clear();
181
3634
        return true;
182
    }
183
18500
    else if (mLine == "#, fuzzy")
184
    {   // check for fuzzy translation
185
2
        mSkipId = true;
186
4
        mLine.clear();
187
2
        return true;
188
    }
189
    // stop reading if we don't read msgid before
190
18496
    return mMsgId.empty();
191
}
192
193
7338
bool PoParser::readMsgStr()
194
{
195
    // normal msgstr
196
29352
    const std::string msgStr1("msgstr \"");
197
    // plurals first msgstr
198
29352
    const std::string msgStr2("msgstr[0] \"");
199
200
    // check if in reading process
201
7338
    if (mReadingStr)
202
    {
203
        // if we get empty line in file then stop reading
204
7416
        if (mLine.empty())
205
        {
206
3622
            mReadingStr = false;
207
3622
            return false;
208
        }
209
86
        if (checkLine())
210
        {
211
            // reading text from: "text"
212
240
            mMsgStr.append(mLine.substr(1, mLine.size() - 2));
213
160
            mLine.clear();
214
80
            return true;
215
        }
216
        // stop reading in other case
217
6
        mReadingStr = false;
218
    }
219
    else
220
    {
221
        // check line start from msgstr "
222

3630
        if (strStartWith(mLine, msgStr1))
223
        {
224
3624
            mReadingStr = true;
225
3624
            const size_t msgStr1Size = msgStr1.size();
226
            // reading text from: msgstr "text"
227
7248
            mMsgStr.append(mLine.substr(msgStr1Size,
228
10872
                mLine.size() - 1 - msgStr1Size));
229
7248
            mLine.clear();
230
3624
            return true;
231
        }
232
        // checl list start from msgstr[0] "
233

6
        else if (strStartWith(mLine, msgStr2))
234
        {
235
6
            mReadingStr = true;
236
6
            const size_t msgStr2Size = msgStr2.size();
237
            // reading text from: msgstr[0] "text"
238
12
            mMsgStr.append(mLine.substr(msgStr2Size,
239
18
                mLine.size() - 1 - msgStr2Size));
240
12
            mLine.clear();
241
6
            return true;
242
        }
243
    }
244
245
    // stop reading in other case
246
    return false;
247
}
248
249
bool PoParser::checkLine() const
250
{
251
3990
    const size_t sz = mLine.size();
252
    // check is line in format: "text"
253




8332
    return sz > 2 && mLine[0] == '\"' && mLine[sz - 1] == '\"';
254
}
255
256
102
PoDict *PoParser::getEmptyDict()
257
{
258

408
    return new PoDict("");
259
}
260
261
bool PoParser::checkLang(const std::string &lang)
262
{
263
    // check is po file exists
264
    return VirtFs::exists(getFileName(lang));
265
}
266
267
std::string PoParser::getFileName(const std::string &lang)
268
{
269
    // get po file name from lang name
270
//    logger->log("getFileName: translations/%s.po", lang.c_str());
271
3
    return strprintf("translations/%s.po", lang.c_str());
272
}
273
274
3
PoDict *PoParser::getDict() const
275
{
276
3
    return new PoDict(mLang);
277
}
278
279
7144
void PoParser::convertStr(std::string &str)
280
{
281
7144
    if (str.empty())
282
        return;
283
284

50008
    replaceAll(str, "\\n", "\n");
285

50008
    replaceAll(str, "\\\"", "\"");
286

50008
    replaceAll(str, "\\\\", "\\");
287
}