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/utf8.h>
10 #include <limits>
11 #include <sys/types.h>
12 #include <sys/wait.h>
13 #include <unistd.h>
14 
15 #include "default_tables.h"
16 #include "utils.h"
17 #include <fcitx-utils/stringutils.h>
18 
utf8_string_substr(const std::string & s,size_t start,size_t len)19 std::string util::utf8_string_substr(const std::string &s, size_t start,
20                                      size_t len) {
21     auto iter = fcitx::utf8::nextNChar(s.begin(), start);
22     auto end = fcitx::utf8::nextNChar(iter, len);
23     std::string result(iter, end);
24     return result;
25 }
26 
match_key_event(const fcitx::KeyList & hotkey,const fcitx::Key & _key,fcitx::KeyStates ignore_mask)27 bool util::match_key_event(const fcitx::KeyList &hotkey, const fcitx::Key &_key,
28                            fcitx::KeyStates ignore_mask) {
29     fcitx::Key key = fcitx::Key(_key.sym(), _key.states() & ~ignore_mask);
30     return key.checkKeyList(hotkey);
31 }
32 
split_string(std::string & str,std::vector<std::string> & str_list,char * delim,int num)33 void util::split_string(std::string &str, std::vector<std::string> &str_list,
34                         char *delim, int num) {
35     std::string::size_type start = 0, end;
36 
37     for (int i = 0; (num > 0 && i < num) || start < str.length(); i++) {
38         end = str.find(delim, start);
39         if ((num > 0 && i == num - 1) || (end == std::string::npos))
40             end = str.length();
41 
42         if (start < str.length()) {
43             str_list.push_back(str.substr(start, end - start));
44             start = end + strlen(delim);
45         } else {
46             str_list.push_back(std::string());
47         }
48     }
49 }
50 
convert_to_wide(const std::string & str)51 std::string util::convert_to_wide(const std::string &str) {
52     std::string wide;
53     for (unsigned int i = 0; i < str.length(); i++) {
54         int c = str[i];
55         char cc[2];
56         cc[0] = c;
57         cc[1] = '\0';
58         bool found = false;
59 
60         for (unsigned int j = 0; fcitx_anthy_wide_table[j].code; j++) {
61             if (fcitx_anthy_wide_table[j].code &&
62                 *fcitx_anthy_wide_table[j].code == c) {
63                 wide += fcitx_anthy_wide_table[j].wide;
64                 found = true;
65                 break;
66             }
67         }
68 
69         if (!found)
70             wide += cc;
71     }
72     return wide;
73 }
74 
convert_to_half(const std::string & str)75 std::string util::convert_to_half(const std::string &str) {
76     std::string half;
77     for (unsigned int i = 0; i < fcitx::utf8::length(str); i++) {
78         std::string wide = util::utf8_string_substr(str, i, 1);
79         bool found = false;
80 
81         for (unsigned int j = 0; fcitx_anthy_wide_table[j].code; j++) {
82             if (fcitx_anthy_wide_table[j].wide &&
83                 wide == fcitx_anthy_wide_table[j].wide) {
84                 half += fcitx_anthy_wide_table[j].code;
85                 found = true;
86                 break;
87             }
88         }
89 
90         if (!found)
91             half += wide;
92     }
93     return half;
94 }
95 
convert_to_katakana(const std::string & hira,bool half)96 std::string util::convert_to_katakana(const std::string &hira, bool half) {
97     std::string kata;
98     for (unsigned int i = 0; i < fcitx::utf8::length(hira); i++) {
99         std::string tmpwide;
100         bool found = false;
101 
102         HiraganaKatakanaRule *table = fcitx_anthy_hiragana_katakana_table;
103 
104         for (unsigned int j = 0; table[j].hiragana; j++) {
105             tmpwide = table[j].hiragana;
106             if (util::utf8_string_substr(hira, i, 1) == tmpwide) {
107                 if (half)
108                     kata += table[j].half_katakana;
109                 else
110                     kata += table[j].katakana;
111                 found = true;
112                 break;
113             }
114         }
115 
116         if (!found)
117             kata += util::utf8_string_substr(hira, i, 1);
118     }
119     return kata;
120 }
121 
key_is_keypad(const fcitx::Key & key)122 bool util::key_is_keypad(const fcitx::Key &key) {
123     switch (key.sym()) {
124     case FcitxKey_KP_Equal:
125     case FcitxKey_KP_Multiply:
126     case FcitxKey_KP_Add:
127     case FcitxKey_KP_Separator:
128     case FcitxKey_KP_Subtract:
129     case FcitxKey_KP_Decimal:
130     case FcitxKey_KP_Divide:
131     case FcitxKey_KP_0:
132     case FcitxKey_KP_1:
133     case FcitxKey_KP_2:
134     case FcitxKey_KP_3:
135     case FcitxKey_KP_4:
136     case FcitxKey_KP_5:
137     case FcitxKey_KP_6:
138     case FcitxKey_KP_7:
139     case FcitxKey_KP_8:
140     case FcitxKey_KP_9:
141         return true;
142     default:
143         return false;
144     }
145 }
146 
keypad_to_string(const fcitx::KeyEvent & key)147 std::string util::keypad_to_string(const fcitx::KeyEvent &key) {
148     char raw[2];
149 
150     switch (key.rawKey().sym()) {
151     case FcitxKey_KP_Equal:
152         raw[0] = '=';
153         break;
154 
155     case FcitxKey_KP_Multiply:
156         raw[0] = '*';
157         break;
158 
159     case FcitxKey_KP_Add:
160         raw[0] = '+';
161         break;
162 
163     case FcitxKey_KP_Separator:
164         raw[0] = ',';
165         break;
166 
167     case FcitxKey_KP_Subtract:
168         raw[0] = '-';
169         break;
170 
171     case FcitxKey_KP_Decimal:
172         raw[0] = '.';
173         break;
174 
175     case FcitxKey_KP_Divide:
176         raw[0] = '/';
177         break;
178 
179     case FcitxKey_KP_0:
180     case FcitxKey_KP_1:
181     case FcitxKey_KP_2:
182     case FcitxKey_KP_3:
183     case FcitxKey_KP_4:
184     case FcitxKey_KP_5:
185     case FcitxKey_KP_6:
186     case FcitxKey_KP_7:
187     case FcitxKey_KP_8:
188     case FcitxKey_KP_9:
189         raw[0] = '0' + key.rawKey().sym() - FcitxKey_KP_0;
190         break;
191 
192     default:
193         raw[0] = util::get_ascii_code(key);
194         break;
195     }
196 
197     raw[1] = '\0';
198     return raw;
199 }
200 
launch_program(std::string command)201 void util::launch_program(std::string command) {
202     if (command.empty())
203         return;
204 
205     /* split string */
206     auto array = fcitx::stringutils::split(command, FCITX_WHITESPACE);
207 
208     if (array.size() <= 0)
209         return;
210 
211     fcitx::startProcess(array);
212 }
213 
surrounding_get_safe_delta(uint from,uint to,int32_t * delta)214 bool util::surrounding_get_safe_delta(uint from, uint to, int32_t *delta) {
215     const int64_t kInt32AbsMax =
216         llabs(static_cast<int64_t>(std::numeric_limits<int32_t>::max()));
217     const int64_t kInt32AbsMin =
218         llabs(static_cast<int64_t>(std::numeric_limits<int32_t>::min()));
219     const int64_t kInt32SafeAbsMax = std::min(kInt32AbsMax, kInt32AbsMin);
220 
221     const int64_t diff = static_cast<int64_t>(from) - static_cast<int64_t>(to);
222     if (llabs(diff) > kInt32SafeAbsMax) {
223         return false;
224     }
225 
226     *delta = static_cast<int32_t>(diff);
227     return true;
228 }
229 
230 // Returns true if |surrounding_text| contains |selected_text|
231 // from |cursor_pos| to |*anchor_pos|.
232 // Otherwise returns false.
search_anchor_pos_forward(const std::string & surrounding_text,const std::string & selected_text,size_t selected_chars_len,uint cursor_pos,uint * anchor_pos)233 static bool search_anchor_pos_forward(const std::string &surrounding_text,
234                                       const std::string &selected_text,
235                                       size_t selected_chars_len,
236                                       uint cursor_pos, uint *anchor_pos) {
237 
238     size_t len = fcitx::utf8::length(surrounding_text);
239     if (len < cursor_pos) {
240         return false;
241     }
242 
243     size_t offset =
244         fcitx::utf8::ncharByteLength(surrounding_text.begin(), cursor_pos);
245 
246     if (surrounding_text.compare(offset, selected_text.size(), selected_text) !=
247         0) {
248         return false;
249     }
250     *anchor_pos = cursor_pos + selected_chars_len;
251     return true;
252 }
253 
254 // Returns true if |surrounding_text| contains |selected_text|
255 // from |*anchor_pos| to |cursor_pos|.
256 // Otherwise returns false.
search_anchor_pos_backward(const std::string & surrounding_text,const std::string & selected_text,size_t selected_chars_len,uint cursor_pos,uint * anchor_pos)257 bool search_anchor_pos_backward(const std::string &surrounding_text,
258                                 const std::string &selected_text,
259                                 size_t selected_chars_len, uint cursor_pos,
260                                 uint *anchor_pos) {
261     if (cursor_pos < selected_chars_len) {
262         return false;
263     }
264 
265     // Skip |iter| to (potential) anchor pos.
266     const uint skip_count = cursor_pos - selected_chars_len;
267     if (skip_count > cursor_pos) {
268         return false;
269     }
270     size_t offset =
271         fcitx::utf8::ncharByteLength(surrounding_text.begin(), skip_count);
272 
273     if (surrounding_text.compare(offset, selected_text.size(), selected_text) !=
274         0) {
275         return false;
276     }
277     *anchor_pos = skip_count;
278     return true;
279 }
280 
surrounding_get_anchor_pos_from_selection(const std::string & surrounding_text,const std::string & selected_text,uint cursor_pos,uint * anchor_pos)281 bool util::surrounding_get_anchor_pos_from_selection(
282     const std::string &surrounding_text, const std::string &selected_text,
283     uint cursor_pos, uint *anchor_pos) {
284     if (surrounding_text.empty()) {
285         return false;
286     }
287 
288     if (selected_text.empty()) {
289         return false;
290     }
291 
292     const size_t selected_chars_len = fcitx::utf8::length(selected_text);
293 
294     if (search_anchor_pos_forward(surrounding_text, selected_text,
295                                   selected_chars_len, cursor_pos, anchor_pos)) {
296         return true;
297     }
298 
299     return search_anchor_pos_backward(surrounding_text, selected_text,
300                                       selected_chars_len, cursor_pos,
301                                       anchor_pos);
302 }
303 
selection_keys()304 const fcitx::KeyList &util::selection_keys() {
305     const static fcitx::KeyList selectionKeys{
306         fcitx::Key(FcitxKey_1), fcitx::Key(FcitxKey_2), fcitx::Key(FcitxKey_3),
307         fcitx::Key(FcitxKey_4), fcitx::Key(FcitxKey_5), fcitx::Key(FcitxKey_6),
308         fcitx::Key(FcitxKey_7), fcitx::Key(FcitxKey_8), fcitx::Key(FcitxKey_9),
309         fcitx::Key(FcitxKey_0)};
310     return selectionKeys;
311 }
312