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