1 /** @file scim_input_group.cpp
2 */
3
4 /*
5 * Smart Common Input Method
6 *
7 * Copyright (c) 2005 James Su <suzhe@tsinghua.org.cn>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 *
23 * $Id: scim_input_group.cpp,v 1.6 2005/11/21 06:40:30 suzhe Exp $
24 *
25 */
26
27 #include <cstdio>
28 #include <cstdlib>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <dirent.h>
32 #include <unistd.h>
33 #include "scim_input_group.h"
34
35 #ifndef SCIM_INPUT_PAD_DATADIR
36 #define SCIM_INPUT_PAD_DATADIR "/usr/share/scim/input-pad"
37 #endif
38
39 #ifndef SCIM_INPUT_PAD_USER_DATADIR
40 #define SCIM_INPUT_PAD_USER_DATADIR "/.scim/input-pad"
41 #endif
42
43 // Helper functions
44 static inline String
trim_blank(const String & str)45 trim_blank (const String &str)
46 {
47 String::size_type begin, len;
48
49 begin = str.find_first_not_of (" \t\n\v");
50
51 if (begin == String::npos)
52 return String ();
53
54 len = str.find_last_not_of (" \t\n\v");
55
56 if (len != String::npos)
57 len = len - begin + 1;
58
59 return str.substr (begin, len);
60 }
61
62 static inline String
get_param_portion(const String & str,const String & delim="=")63 get_param_portion (const String &str, const String &delim = "=")
64 {
65 String ret = str;
66 String::size_type pos = ret.find_first_of (String (" \t\v") + delim);
67
68 if (pos != String::npos)
69 ret.erase (pos, String::npos);
70
71 return ret;
72 }
73
74 static inline String
get_value_portion(const String & str,const String & delim="=")75 get_value_portion (const String &str, const String &delim = "=")
76 {
77 String ret = str;
78 String::size_type pos;
79
80 pos = ret.find_first_of (delim);
81
82 if (pos != String::npos)
83 ret.erase (0, pos + 1);
84
85 pos = ret.find_first_not_of(" \t\v");
86
87 if (pos != String::npos)
88 ret.erase (0, pos);
89
90 pos = ret.find_last_not_of(" \t\v");
91
92 if (pos != String::npos)
93 ret.erase (pos + 1, String::npos);
94
95 return ret;
96 }
97
98 static inline String
get_line(FILE * fp)99 get_line (FILE *fp)
100 {
101 char temp [4096];
102 String res;
103
104 while (fp && !feof (fp)) {
105 if (!fgets (temp, 4096, fp)) break;
106
107 res = trim_blank (String (temp));
108
109 if (res.length () &&
110 !(res.length () >= 3 && res.substr (0, 3) == String ("###")))
111 return res;
112 }
113
114 return String ();
115 }
116
117 static const char scim_input_pad_header [] = "SCIM_Input_Pad";
118 static const char scim_input_pad_version [] = "VERSION_1_0";
119
120 static InputTablePointer
load_one_table_from_file(FILE * fp)121 load_one_table_from_file (FILE *fp)
122 {
123 if (!fp || feof (fp)) return InputTablePointer (0);
124
125 String str;
126 std::vector <String> strvec;
127 std::vector <String>::iterator it;
128
129 InputTablePointer tbl = new InputTable ();
130
131 String cur_lang = scim_get_locale_language (scim_get_current_locale ());
132
133 while (!feof (fp)) {
134 str = get_line (fp);
135
136 if (str == String ("END_TABLE")) {
137 if (tbl->number_of_elements ()) return tbl;
138 return InputTablePointer (0);
139 }
140
141 if (str.length () > 5 && str.substr (0, 4) == "NAME") {
142 String value, param;
143 param = get_param_portion (str);
144 value = get_value_portion (str);
145
146 if (param == "NAME") {
147 if (value.length () && tbl->get_name ().length () == 0) tbl->set_name (value);
148 } else if (param.length () > 7 && param [4] == '[' && param [param.length () - 1] == ']') {
149 String lang = param.substr (5, param.length () - 6);
150 if (lang.length () && scim_get_normalized_language (lang) == cur_lang && value.length ())
151 tbl->set_name (value);
152 }
153
154 continue;
155 }
156
157 if (str.length () > 8 && str.substr (0, 7) == "COLUMNS") {
158 String value, param;
159 param = get_param_portion (str);
160 value = get_value_portion (str);
161
162 if (param == "COLUMNS") {
163 unsigned int columns = strtol (value.c_str (), 0, 10);
164 tbl->set_columns (columns);
165 }
166
167 continue;
168 }
169
170 if (scim_split_string_list (strvec, str, ' ') > 0) {
171 for (it = strvec.begin(); it != strvec.end (); ++it) {
172 InputElement elm;
173
174 if (it->length () > 1 && (*it)[0] == '*') {
175 elm.type = INPUT_ELEMENT_KEY;
176 elm.data = it->substr (1, it->length () - 1);
177 } else if (it->length () > 2 && (*it)[0] == '0' && ((*it)[1] == 'x' || (*it)[1] == 'X')) {
178 ucs4_t wc = strtol (it->c_str () + 2, 0, 16);
179 if (wc > 0) {
180 char mb [8];
181 mb [utf8_wctomb ((unsigned char*)mb, wc, 8)] = 0;
182 elm.type = INPUT_ELEMENT_STRING;
183 elm.data = String (mb);
184 }
185 } else if (it->length () > 0 && (*it)[0] != '*') {
186 elm.type = INPUT_ELEMENT_STRING;
187 elm.data = (*it);
188 }
189
190 if (elm.type != INPUT_ELEMENT_NONE || (it->length () == 1 && (*it)[0] == '*'))
191 tbl->append_element (elm);
192 }
193 }
194 }
195
196 return InputTablePointer (0);
197 }
198
199 static void
save_one_table_to_file(FILE * fp,const InputTablePointer & table)200 save_one_table_to_file (FILE *fp, const InputTablePointer &table)
201 {
202 if (!fp || table.null ()) return;
203
204 fprintf (fp, "BEGIN_TABLE\n");
205 fprintf (fp, "NAME=%s\n", table->get_name ().c_str ());
206 fprintf (fp, "COLUMNS=%u\n\n", table->get_columns ());
207
208 for (size_t i = 0; i < table->number_of_elements (); ++i) {
209 const InputElement &elm = table->get_element (i);
210
211 if (elm.type == INPUT_ELEMENT_STRING)
212 fprintf (fp, "%s ", elm.data.c_str ());
213 else if (elm.type == INPUT_ELEMENT_KEY)
214 fprintf (fp, "*%s ", elm.data.c_str ());
215 else
216 fprintf (fp, "* ");
217
218 if ((i + 1) % table->get_columns () == 0)
219 fprintf (fp, "\n");
220 }
221
222 fprintf (fp, "\nEND_TABLE\n\n");
223 }
224
225 static InputGroupPointer
load_one_group_from_file(FILE * fp)226 load_one_group_from_file (FILE *fp)
227 {
228 if (!fp || feof (fp)) return InputGroupPointer (0);
229
230 String str, value, param;
231 String cur_lang = scim_get_locale_language (scim_get_current_locale ());
232
233 InputGroupPointer grp = new InputGroup ();
234
235 while (!feof (fp)) {
236 str = get_line (fp);
237 if (str == String ("BEGIN_TABLE")) {
238 InputTablePointer tbl = load_one_table_from_file (fp);
239 if (!tbl.null () && tbl->number_of_elements () > 0)
240 grp->append_table (tbl);
241
242 continue;
243 }
244
245 if (str == String ("END_GROUP")) {
246 if (grp->number_of_tables ()) return grp;
247 return InputGroupPointer (0);
248 }
249
250 param = get_param_portion (str);
251 value = get_value_portion (str);
252
253 if (param == "NAME") {
254 if (value.length () && grp->get_name ().length () == 0) grp->set_name (value);
255 } else if (param.length () > 7 && param.substr (0,5) == "NAME[" && param [param.length () - 1] == ']') {
256 String lang = param.substr (5, param.length () - 6);
257 if (lang.length () && scim_get_normalized_language (lang) == cur_lang && value.length ())
258 grp->set_name (value);
259 }
260 }
261 return InputGroupPointer (0);
262 }
263
264 static void
save_one_group_to_file(FILE * fp,const InputGroupPointer & group)265 save_one_group_to_file (FILE *fp, const InputGroupPointer &group)
266 {
267 if (!fp || group.null ()) return;
268
269 fprintf (fp, "BEGIN_GROUP\n");
270 fprintf (fp, "NAME=%s\n\n", group->get_name ().c_str ());
271
272 for (size_t i = 0; i < group->number_of_tables (); ++i) {
273 save_one_table_to_file (fp, group->get_table (i));
274 }
275
276 fprintf (fp, "END_GROUP\n\n");
277 }
278
279 int
load_input_group_file(const String & file_name,std::vector<InputGroupPointer> & groups)280 load_input_group_file (const String &file_name, std::vector <InputGroupPointer> &groups)
281 {
282 FILE *fp = fopen (file_name.c_str (), "rb");
283
284 if (!fp) return 0;
285
286 if (get_line (fp) != String (scim_input_pad_header) ||
287 get_line (fp) != String (scim_input_pad_version))
288 return 0;
289
290 while (!feof (fp)) {
291 if (get_line (fp) == String ("BEGIN_GROUP")) {
292 InputGroupPointer grp = load_one_group_from_file (fp);
293 if (!grp.null () && grp->number_of_tables () > 0) {
294 groups.push_back (grp);
295 }
296 }
297 }
298 return groups.size ();
299 }
300
301 bool
save_input_group_file(const String & file_name,const std::vector<InputGroupPointer> & groups)302 save_input_group_file (const String &file_name, const std::vector <InputGroupPointer> &groups)
303 {
304 if (groups.size () == 0) return false;
305
306 FILE *fp = fopen (file_name.c_str (), "wb");
307
308 if (!fp) return false;
309
310 fprintf (fp, "%s\n", scim_input_pad_header);
311 fprintf (fp, "%s\n\n", scim_input_pad_version);
312
313 for (size_t i = 0; i < groups.size (); ++i) {
314 save_one_group_to_file (fp, groups [i]);
315 }
316
317 fclose (fp);
318
319 return true;
320 }
321
322 static void
get_input_group_file_list(std::vector<String> & file_list,const String & path)323 get_input_group_file_list (std::vector<String> &file_list, const String &path)
324 {
325 file_list.clear ();
326
327 DIR *dir = opendir (path.c_str ());
328 if (dir != NULL) {
329 struct dirent *file = readdir (dir);
330 while (file != NULL) {
331 struct stat filestat;
332 String absfn = path + SCIM_PATH_DELIM_STRING + file->d_name;
333 stat (absfn.c_str (), &filestat);
334
335 if (S_ISREG (filestat.st_mode))
336 file_list.push_back (absfn);
337
338 file = readdir (dir);
339 }
340 closedir (dir);
341 }
342 }
343
344 int
load_all_input_group_files(std::vector<InputGroupPointer> & groups)345 load_all_input_group_files (std::vector <InputGroupPointer> &groups)
346 {
347 std::vector<String> file_list;
348
349 groups.clear ();
350
351 get_input_group_file_list (file_list, SCIM_INPUT_PAD_DATADIR);
352
353 for (size_t i = 0; i < file_list.size (); ++i) {
354 load_input_group_file (file_list [i], groups);
355 }
356
357 get_input_group_file_list (file_list, scim_get_home_dir () + SCIM_INPUT_PAD_USER_DATADIR);
358
359 for (size_t i = 0; i < file_list.size (); ++i)
360 load_input_group_file (file_list [i], groups);
361
362 return groups.size ();
363 }
364
365 /*
366 vi:ts=4:nowrap:ai:expandtab
367 */
368
369