1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  *  Copyright (C) 2005 Takuro Ashie
4  *  Copyright (C) 2012 CSSlayer
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2, or (at your option)
9  *  any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 #include <string.h>
22 #include <sys/wait.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25 #include <fcitx-utils/utf8.h>
26 #include <limits>
27 
28 #include "utils.h"
29 #include "default_tables.h"
30 
31 size_t
util_utf8_string_length(const std::string & s)32 util_utf8_string_length(const std::string& s)
33 {
34     return fcitx_utf8_strlen(s.c_str());
35 }
36 
37 std::string
util_utf8_string_substr(const std::string & s,size_t start,size_t len)38 util_utf8_string_substr(const std::string& s, size_t start, size_t len)
39 {
40     char* cs = strdup(s.c_str());
41     char* startp = fcitx_utf8_get_nth_char(cs, start);
42     char* endp = fcitx_utf8_get_nth_char(startp, len);
43     std::string result(startp, endp - startp);
44     free(cs);
45     return result;
46 }
47 
48 bool
util_match_key_event(const FcitxHotkey * hotkey,const KeyEvent & key,uint32_t ignore_mask)49 util_match_key_event (const FcitxHotkey* hotkey, const KeyEvent &key,
50                       uint32_t ignore_mask)
51 {
52     FcitxKeySym simpsym;
53     unsigned int simpstate;
54     FcitxHotkeyGetKey(key.sym, key.state, &simpsym, &simpstate);
55     return FcitxHotkeyIsHotKey(simpsym, simpstate & ~ignore_mask, hotkey);
56 }
57 
58 void
util_split_string(std::string & str,std::vector<std::string> & str_list,char * delim,int num)59 util_split_string (std::string &str, std::vector<std::string> &str_list,
60                    char *delim, int num)
61 {
62     std::string::size_type start = 0, end;
63 
64     for (int i = 0; (num > 0 && i < num) || start < str.length (); i++) {
65         end = str.find (delim, start);
66         if ((num > 0 && i == num - 1) || (end == std::string::npos))
67             end = str.length ();
68 
69         if (start < str.length ()) {
70             str_list.push_back (str.substr (start, end - start));
71             start = end + strlen (delim);
72         } else {
73             str_list.push_back (std::string ());
74         }
75     }
76 }
77 
78 void
util_convert_to_wide(std::string & wide,const std::string & str)79 util_convert_to_wide (std::string & wide, const std::string & str)
80 {
81     for (unsigned int i = 0; i < str.length (); i++) {
82         int c = str[i];
83         char cc[2];
84         cc[0] = c;
85         cc[1] = '\0';
86         bool found = false;
87 
88         for (unsigned int j = 0; fcitx_anthy_wide_table[j].code; j++) {
89             if ( fcitx_anthy_wide_table[j].code &&
90                     *fcitx_anthy_wide_table[j].code == c)
91             {
92                 wide += fcitx_anthy_wide_table[j].wide;
93                 found = true;
94                 break;
95             }
96         }
97 
98         if (!found)
99             wide += cc;
100     }
101 }
102 
103 void
util_convert_to_half(std::string & half,const std::string & str)104 util_convert_to_half (std::string & half, const std::string & str)
105 {
106     for (unsigned int i = 0; i < util_utf8_string_length(str); i++) {
107         std::string wide = util_utf8_string_substr(str, i, 1);
108         bool found = false;
109 
110         for (unsigned int j = 0; fcitx_anthy_wide_table[j].code; j++) {
111             if (fcitx_anthy_wide_table[j].wide &&
112                     wide == fcitx_anthy_wide_table[j].wide)
113             {
114                 half += fcitx_anthy_wide_table[j].code;
115                 found = true;
116                 break;
117             }
118         }
119 
120         if (!found)
121             half += wide;
122     }
123 }
124 
125 void
util_convert_to_katakana(std::string & kata,const std::string & hira,bool half)126 util_convert_to_katakana (std::string & kata,
127                           const std::string & hira,
128                           bool half)
129 {
130     for (unsigned int i = 0; i < util_utf8_string_length(hira); i++) {
131         std::string tmpwide;
132         bool found = false;
133 
134         HiraganaKatakanaRule *table = fcitx_anthy_hiragana_katakana_table;
135 
136         for (unsigned int j = 0; table[j].hiragana; j++) {
137             tmpwide = table[j].hiragana;
138             if (util_utf8_string_substr(hira, i, 1) == tmpwide) {
139                 if (half)
140                     kata += table[j].half_katakana;
141                 else
142                     kata += table[j].katakana;
143                 found = true;
144                 break;
145             }
146         }
147 
148         if (!found)
149             kata += util_utf8_string_substr(hira, i, 1);
150     }
151 }
152 
153 bool
util_key_is_keypad(const KeyEvent & key)154 util_key_is_keypad (const KeyEvent &key)
155 {
156     switch (key.sym) {
157     case FcitxKey_KP_Equal:
158     case FcitxKey_KP_Multiply:
159     case FcitxKey_KP_Add:
160     case FcitxKey_KP_Separator:
161     case FcitxKey_KP_Subtract:
162     case FcitxKey_KP_Decimal:
163     case FcitxKey_KP_Divide:
164     case FcitxKey_KP_0:
165     case FcitxKey_KP_1:
166     case FcitxKey_KP_2:
167     case FcitxKey_KP_3:
168     case FcitxKey_KP_4:
169     case FcitxKey_KP_5:
170     case FcitxKey_KP_6:
171     case FcitxKey_KP_7:
172     case FcitxKey_KP_8:
173     case FcitxKey_KP_9:
174         return true;
175     default:
176         return false;
177     }
178 }
179 
180 void
util_keypad_to_string(std::string & str,const KeyEvent & key)181 util_keypad_to_string (std::string &str, const KeyEvent &key)
182 {
183     char raw[2];
184 
185     switch (key.sym) {
186     case FcitxKey_KP_Equal:
187         raw[0] = '=';
188         break;
189 
190     case FcitxKey_KP_Multiply:
191         raw[0] = '*';
192         break;
193 
194     case FcitxKey_KP_Add:
195         raw[0] = '+';
196         break;
197 
198     case FcitxKey_KP_Separator:
199         raw[0] = ',';
200         break;
201 
202     case FcitxKey_KP_Subtract:
203         raw[0] = '-';
204         break;
205 
206     case FcitxKey_KP_Decimal:
207         raw[0] = '.';
208         break;
209 
210     case FcitxKey_KP_Divide:
211         raw[0] = '/';
212         break;
213 
214     case FcitxKey_KP_0:
215     case FcitxKey_KP_1:
216     case FcitxKey_KP_2:
217     case FcitxKey_KP_3:
218     case FcitxKey_KP_4:
219     case FcitxKey_KP_5:
220     case FcitxKey_KP_6:
221     case FcitxKey_KP_7:
222     case FcitxKey_KP_8:
223     case FcitxKey_KP_9:
224         raw[0] = '0' + key.sym - FcitxKey_KP_0;
225         break;
226 
227     default:
228         if (isprint (key.get_ascii_code()))
229             raw[0] = key.get_ascii_code();
230         else
231             raw[0] = '\0';
232         break;
233     }
234 
235     raw[1] = '\0';
236     str = raw;
237 }
238 
239 void
util_launch_program(const char * command)240 util_launch_program (const char *command)
241 {
242     if (!command) return;
243 
244     /* split string */
245     unsigned int len = strlen (command);
246     char tmp[len + 1];
247     strncpy (tmp, command, len);
248     tmp[len] = '\0';
249 
250     char *str = tmp;
251     std::vector<char *> array;
252 
253     for (unsigned int i = 0; i < len + 1; i++) {
254         if (!tmp[i] || isspace (tmp[i])) {
255             if (*str) {
256                 tmp[i] = '\0';
257                 array.push_back (str);
258             }
259             str = tmp + i + 1;
260         }
261     }
262 
263     if (array.size () <= 0) return;
264     array.push_back (NULL);
265 
266     char **args = (char**) fcitx_utils_malloc0(sizeof(char*) * array.size());
267     for (unsigned int i = 0; i < array.size (); i++)
268         args[i] = array[i];
269 
270     fcitx_utils_start_process(args);
271 
272     free(args);
273 }
274 
util_surrounding_get_safe_delta(uint from,uint to,int32_t * delta)275 bool util_surrounding_get_safe_delta(uint from, uint to, int32_t *delta) {
276     const int64_t kInt32AbsMax =
277         llabs(static_cast<int64_t>(std::numeric_limits<int32_t>::max()));
278     const int64_t kInt32AbsMin =
279         llabs(static_cast<int64_t>(std::numeric_limits<int32_t>::min()));
280     const int64_t kInt32SafeAbsMax =
281         std::min(kInt32AbsMax, kInt32AbsMin);
282 
283     const int64_t diff = static_cast<int64_t>(from) - static_cast<int64_t>(to);
284     if (llabs(diff) > kInt32SafeAbsMax) {
285         return false;
286     }
287 
288     *delta = static_cast<int32_t>(diff);
289     return true;
290 }
291 
292 // Returns true if |surrounding_text| contains |selected_text|
293 // from |cursor_pos| to |*anchor_pos|.
294 // 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)295 static bool search_anchor_pos_forward(
296     const std::string &surrounding_text,
297     const std::string &selected_text,
298     size_t selected_chars_len,
299     uint cursor_pos,
300     uint *anchor_pos) {
301 
302     size_t len = fcitx_utf8_strlen(surrounding_text.c_str());
303     if (len < cursor_pos) {
304         return false;
305     }
306 
307     size_t offset = fcitx_utf8_get_nth_char(surrounding_text.c_str(), cursor_pos) - surrounding_text.c_str();
308 
309     if (surrounding_text.compare(offset, selected_text.size(), selected_text) != 0) {
310         return false;
311     }
312     *anchor_pos = cursor_pos + selected_chars_len;
313     return true;
314 }
315 
316 // Returns true if |surrounding_text| contains |selected_text|
317 // from |*anchor_pos| to |cursor_pos|.
318 // 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)319 bool search_anchor_pos_backward(
320     const std::string &surrounding_text,
321     const std::string &selected_text,
322     size_t selected_chars_len,
323     uint cursor_pos,
324     uint *anchor_pos) {
325     if (cursor_pos < selected_chars_len) {
326         return false;
327     }
328 
329     // Skip |iter| to (potential) anchor pos.
330     const uint skip_count = cursor_pos - selected_chars_len;
331     if (skip_count > cursor_pos) {
332         return false;
333     }
334     size_t offset = fcitx_utf8_get_nth_char(surrounding_text.c_str(), skip_count) - surrounding_text.c_str();
335 
336     if (surrounding_text.compare(offset, selected_text.size(), selected_text) != 0) {
337         return false;
338     }
339     *anchor_pos = skip_count;
340     return true;
341 }
342 
util_surrounding_get_anchor_pos_from_selection(const std::string & surrounding_text,const std::string & selected_text,uint cursor_pos,uint * anchor_pos)343 bool util_surrounding_get_anchor_pos_from_selection(
344     const std::string &surrounding_text,
345     const std::string &selected_text,
346     uint cursor_pos,
347     uint *anchor_pos) {
348     if (surrounding_text.empty()) {
349         return false;
350     }
351 
352     if (selected_text.empty()) {
353         return false;
354     }
355 
356     const size_t selected_chars_len = fcitx_utf8_strlen(selected_text.c_str());
357 
358     if (search_anchor_pos_forward(surrounding_text, selected_text,
359                                   selected_chars_len,
360                                   cursor_pos, anchor_pos)) {
361         return true;
362     }
363 
364     return search_anchor_pos_backward(surrounding_text, selected_text,
365                                       selected_chars_len,
366                                       cursor_pos, anchor_pos);
367 }
368