1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * SPDX-FileCopyrightText: 2005 Takuro Ashie
4 * SPDX-FileCopyrightText: 2012 CSSlayer <wengxt@gmail.com>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 #include <fcitx-utils/log.h>
10 #include <fstream>
11
12 #include "key2kana_table.h"
13 #include "style_file.h"
14 #include <fcitx-utils/charutils.h>
15
16 namespace {
17 const int MAX_LINE_LENGTH = 4096;
18
escape(const std::string & str)19 std::string escape(const std::string &str) {
20 std::string dest = str;
21
22 for (unsigned int i = 0; i < dest.size(); i++) {
23 if (dest[i] == '#' || // for comment
24 dest[i] == '\\' || // for backslash itself
25 dest[i] == '=' || // for separatort
26 dest[i] == '[' || dest[i] == ']' || // for section
27 dest[i] == ',' || // for array
28 dest[i] == ' ' || dest[i] == '\t') // for space
29 {
30 dest.insert(i, "\\");
31 i++;
32 }
33 }
34
35 return dest;
36 }
37
unescape(const std::string & str)38 std::string unescape(const std::string &str) {
39 std::string dest = str;
40
41 for (unsigned int i = 0; i < dest.size(); i++) {
42 if (dest[i] == '\\') {
43 dest.erase(i, 1);
44 if (i < dest.size() && dest[i] == '\\')
45 i++;
46 }
47 }
48
49 return dest;
50 }
51 } // namespace
52
StyleLine(StyleFile * style_file,std::string line)53 StyleLine::StyleLine(StyleFile *style_file, std::string line)
54 : styleFile_(style_file), line_(std::move(line)),
55 type_(StyleLineType::UNKNOWN) {}
56
~StyleLine()57 StyleLine::~StyleLine() {}
58
type()59 StyleLineType StyleLine::type() {
60 if (type_ != StyleLineType::UNKNOWN) {
61 return type_;
62 }
63
64 auto line = fcitx::stringutils::trim(line_);
65 if (line.length() == 0) {
66 type_ = StyleLineType::SPACE;
67 return type_;
68 } else if (line[0] == '#') {
69 type_ = StyleLineType::COMMENT;
70 return type_;
71
72 } else if (line.front() == '[' && line.back() == ']') {
73 type_ = StyleLineType::SECTION;
74 return type_;
75 }
76
77 type_ = StyleLineType::KEY;
78 return type_;
79 }
80
get_section(std::string & section)81 bool StyleLine::get_section(std::string §ion) {
82 if (type() != StyleLineType::SECTION) {
83 return false;
84 }
85
86 auto result = fcitx::stringutils::trim(line_);
87 // remove [ and ]
88 result.pop_back();
89 result = result.substr(1);
90 section = std::move(result);
91
92 return true;
93 }
94
get_key(std::string & key)95 bool StyleLine::get_key(std::string &key) {
96 if (type() != StyleLineType::KEY)
97 return false;
98
99 // skip space ahead.
100 auto spos = line_.find_first_not_of(FCITX_WHITESPACE);
101 if (spos == std::string::npos) {
102 key = std::string();
103 return true;
104 }
105 size_t epos = spos;
106
107 for (epos = spos; epos < line_.length(); epos++) {
108 if (line_[epos] == '\\') {
109 epos++;
110 continue;
111 }
112 if (line_[epos] == '=') {
113 break;
114 }
115 }
116 for (--epos; epos >= spos && fcitx::charutils::isspace(line_[epos]); epos--)
117 ;
118 if (!fcitx::charutils::isspace(line_[epos]))
119 epos++;
120
121 if (spos < epos && epos <= line_.length()) {
122 key = unescape(line_.substr(spos, epos - spos));
123 } else
124 key = std::string();
125
126 return true;
127 }
128
get_value_position(std::string & str)129 static int get_value_position(std::string &str) {
130 unsigned int spos;
131 for (spos = 0; spos < str.length(); spos++) {
132 if (str[spos] == '\\') {
133 spos++;
134 continue;
135 }
136 if (str[spos] == '=') {
137 break;
138 }
139 }
140 if (spos >= str.length())
141 return true;
142 else
143 spos++;
144 for (; spos < str.length() && fcitx::charutils::isspace(str[spos]); spos++)
145 ;
146
147 return spos;
148 }
149
get_value(std::string & value)150 bool StyleLine::get_value(std::string &value) {
151 if (type() != StyleLineType::KEY)
152 return false;
153
154 unsigned int spos = get_value_position(line_);
155 unsigned int epos = line_.length();
156
157 value = unescape(line_.substr(spos, epos - spos));
158
159 return true;
160 }
161
get_value_array(std::vector<std::string> & value)162 bool StyleLine::get_value_array(std::vector<std::string> &value) {
163 if (type() != StyleLineType::KEY)
164 return false;
165
166 unsigned int spos = get_value_position(line_);
167 unsigned int epos = line_.length();
168
169 unsigned int head_of_element = spos;
170 for (unsigned int i = spos; i <= epos; i++) {
171 if (i < epos && line_[i] == '\\') {
172 i++;
173 continue;
174 }
175
176 if (i == epos || line_[i] == ',') {
177 std::string str;
178 if (head_of_element == epos)
179 str = std::string();
180 else
181 str = unescape(
182 line_.substr(head_of_element, i - head_of_element));
183 value.push_back(str);
184 head_of_element = i + 1;
185 }
186 }
187
188 return true;
189 }
190
StyleFile()191 StyleFile::StyleFile() { setupDefaultEntries(); }
192
load(const std::string & filename)193 bool StyleFile::load(const std::string &filename) {
194 clear();
195 setupDefaultEntries();
196
197 std::ifstream in_file(filename);
198 if (!in_file)
199 return false;
200
201 clear();
202
203 sections_.push_back(StyleLines());
204 StyleLines *section = §ions_[0];
205 unsigned int section_id = 0;
206
207 char buf[MAX_LINE_LENGTH];
208 do {
209 in_file.getline(buf, MAX_LINE_LENGTH);
210 if (in_file.eof())
211 break;
212
213 std::string dest = buf;
214 StyleLine line(this, dest);
215 StyleLineType type = line.type();
216
217 if (type == StyleLineType::SECTION) {
218 sections_.push_back(StyleLines());
219 section = §ions_.back();
220 section_id++;
221 }
222
223 section->push_back(line);
224
225 if (section_id == 0) {
226 std::string key;
227 line.get_key(key);
228 if (key == "Title") {
229 line.get_value(title_);
230 }
231 }
232 } while (!in_file.eof());
233
234 in_file.close();
235
236 return true;
237 }
238
clear()239 void StyleFile::clear() {
240 title_ = std::string();
241 sections_.clear();
242 }
243
title() const244 const std::string &StyleFile::title() const { return title_; }
245
getString(std::string & value,std::string section,std::string key)246 bool StyleFile::getString(std::string &value, std::string section,
247 std::string key) {
248 StyleSections::iterator it;
249 for (it = sections_.begin(); it != sections_.end(); it++) {
250 if (it->size() <= 0)
251 continue;
252
253 std::string s, k;
254 (*it)[0].get_section(s);
255
256 if (s != section)
257 continue;
258
259 StyleLines::iterator lit;
260 for (lit = it->begin(); lit != it->end(); lit++) {
261 lit->get_key(k);
262 if (k == key) {
263 lit->get_value(value);
264 return true;
265 }
266 }
267 }
268
269 return false;
270 }
271
getStringArray(std::vector<std::string> & value,std::string section,std::string key)272 bool StyleFile::getStringArray(std::vector<std::string> &value,
273 std::string section, std::string key) {
274 StyleLines *lines = findSection(section);
275 if (!lines)
276 return false;
277
278 // find entry
279 StyleLines::iterator lit;
280 for (lit = lines->begin(); lit != lines->end(); lit++) {
281 std::string k;
282 lit->get_key(k);
283 if (k == key) {
284 lit->get_value_array(value);
285 return true;
286 }
287 }
288
289 return false;
290 }
291
getKeyList(std::vector<std::string> & keys,std::string section)292 bool StyleFile::getKeyList(std::vector<std::string> &keys,
293 std::string section) {
294 StyleLines *lines = findSection(section);
295 if (!lines)
296 return false;
297
298 StyleLines::iterator lit;
299 for (lit = lines->begin(); lit != lines->end(); lit++) {
300 if (lit->type() != StyleLineType::KEY)
301 continue;
302
303 std::string key;
304 lit->get_key(key);
305 keys.push_back(key);
306 }
307 return true;
308 }
309
key2kanaTable(std::string section)310 Key2KanaTable StyleFile::key2kanaTable(std::string section) {
311 Key2KanaTable table(title());
312
313 std::vector<std::string> keys;
314 bool success = getKeyList(keys, section);
315 if (success) {
316 std::vector<std::string>::iterator it;
317 for (it = keys.begin(); it != keys.end(); it++) {
318 std::vector<std::string> array;
319 getStringArray(array, section, *it);
320 table.appendRule(*it, array);
321 }
322 }
323
324 return table;
325 }
326
setupDefaultEntries()327 void StyleFile::setupDefaultEntries() {
328 title_ = "User defined";
329 sections_.push_back(StyleLines());
330
331 sections_.push_back(StyleLines());
332 StyleLines &newsec = sections_.back();
333 std::string str = std::string("Title") + std::string("=") + escape(title_);
334 newsec.push_back(StyleLine(this, str.c_str()));
335 }
336
findSection(const std::string & section)337 StyleLines *StyleFile::findSection(const std::string §ion) {
338 // find section
339 StyleSections::iterator it;
340 for (it = sections_.begin(); it != sections_.end(); it++) {
341 if (it->size() <= 0)
342 continue;
343
344 std::string s;
345 (*it)[0].get_section(s);
346
347 if (s == section)
348 return &(*it);
349 }
350
351 return nullptr;
352 }
353