1 /*
2  * Smart Common Input Method
3  *
4  * Copyright (c) 2002-2005 James Su <suzhe@tsinghua.org.cn>
5  *
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this program; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20  * Boston, MA  02111-1307  USA
21  *
22  * $Id: scim_utility.cpp,v 1.48.2.5 2006/11/02 04:11:51 suzhe Exp $
23  */
24 
25 #define Uses_SCIM_UTILITY
26 #define Uses_SCIM_CONFIG_PATH
27 #define Uses_C_LOCALE
28 #define Uses_C_ICONV
29 #define Uses_C_STDLIB
30 #define Uses_C_STRING
31 
32 #include <langinfo.h>
33 #include <pwd.h>
34 #include <dirent.h>
35 #include <ctype.h>
36 #include <sys/types.h>
37 #include <sys/wait.h>
38 #include <sys/stat.h>
39 #include <unistd.h>
40 #include <stdio.h>
41 #include <time.h>
42 #include <errno.h>
43 
44 #include "scim_private.h"
45 #include "scim.h"
46 
47 namespace scim {
48 
49 int
utf8_mbtowc(ucs4_t * pwc,const unsigned char * src,int src_len)50 utf8_mbtowc (ucs4_t *pwc, const unsigned char *src, int src_len)
51 {
52     if (!pwc)
53         return 0;
54 
55     unsigned char c = src [0];
56 
57     if (c < 0x80) {
58         *pwc = c;
59         return 1;
60     } else if (c < 0xc2) {
61         return RET_ILSEQ;
62     } else if (c < 0xe0) {
63         if (src_len < 2)
64             return RET_TOOFEW(0);
65         if (!((src [1] ^ 0x80) < 0x40))
66             return RET_ILSEQ;
67         *pwc = ((ucs4_t) (c & 0x1f) << 6)
68                  | (ucs4_t) (src [1] ^ 0x80);
69         return 2;
70     } else if (c < 0xf0) {
71         if (src_len < 3)
72             return RET_TOOFEW(0);
73         if (!((src [1] ^ 0x80) < 0x40 && (src [2] ^ 0x80) < 0x40
74                 && (c >= 0xe1 || src [1] >= 0xa0)))
75             return RET_ILSEQ;
76         *pwc = ((ucs4_t) (c & 0x0f) << 12)
77                  | ((ucs4_t) (src [1] ^ 0x80) << 6)
78                  | (ucs4_t) (src [2] ^ 0x80);
79         return 3;
80     } else if (c < 0xf8) {
81         if (src_len < 4)
82             return RET_TOOFEW(0);
83         if (!((src [1] ^ 0x80) < 0x40 && (src [2] ^ 0x80) < 0x40
84                 && (src [3] ^ 0x80) < 0x40
85                 && (c >= 0xf1 || src [1] >= 0x90)))
86             return RET_ILSEQ;
87         *pwc = ((ucs4_t) (c & 0x07) << 18)
88                  | ((ucs4_t) (src [1] ^ 0x80) << 12)
89                  | ((ucs4_t) (src [2] ^ 0x80) << 6)
90                  | (ucs4_t) (src [3] ^ 0x80);
91         return 4;
92     } else if (c < 0xfc) {
93         if (src_len < 5)
94             return RET_TOOFEW(0);
95         if (!((src [1] ^ 0x80) < 0x40 && (src [2] ^ 0x80) < 0x40
96                 && (src [3] ^ 0x80) < 0x40 && (src [4] ^ 0x80) < 0x40
97                 && (c >= 0xf9 || src [1] >= 0x88)))
98             return RET_ILSEQ;
99         *pwc = ((ucs4_t) (c & 0x03) << 24)
100                  | ((ucs4_t) (src [1] ^ 0x80) << 18)
101                  | ((ucs4_t) (src [2] ^ 0x80) << 12)
102                  | ((ucs4_t) (src [3] ^ 0x80) << 6)
103                  | (ucs4_t) (src [4] ^ 0x80);
104         return 5;
105     } else if (c < 0xfe) {
106         if (src_len < 6)
107             return RET_TOOFEW(0);
108         if (!((src [1] ^ 0x80) < 0x40 && (src [2] ^ 0x80) < 0x40
109                 && (src [3] ^ 0x80) < 0x40 && (src [4] ^ 0x80) < 0x40
110                 && (src [5] ^ 0x80) < 0x40
111                 && (c >= 0xfd || src [1] >= 0x84)))
112             return RET_ILSEQ;
113         *pwc = ((ucs4_t) (c & 0x01) << 30)
114                  | ((ucs4_t) (src [1] ^ 0x80) << 24)
115                  | ((ucs4_t) (src [2] ^ 0x80) << 18)
116                  | ((ucs4_t) (src [3] ^ 0x80) << 12)
117                  | ((ucs4_t) (src [4] ^ 0x80) << 6)
118                  | (ucs4_t) (src [5] ^ 0x80);
119         return 6;
120     } else
121         return RET_ILSEQ;
122 }
123 
124 int
utf8_wctomb(unsigned char * dest,ucs4_t wc,int dest_size)125 utf8_wctomb (unsigned char *dest, ucs4_t wc, int dest_size)
126 {
127     if (!dest)
128         return 0;
129 
130     int count;
131     if (wc < 0x80)
132         count = 1;
133     else if (wc < 0x800)
134         count = 2;
135     else if (wc < 0x10000)
136         count = 3;
137     else if (wc < 0x200000)
138         count = 4;
139     else if (wc < 0x4000000)
140         count = 5;
141     else if (wc <= 0x7fffffff)
142         count = 6;
143     else
144         return RET_ILSEQ;
145     if (dest_size < count)
146         return RET_TOOSMALL;
147     switch (count) { /* note: code falls through cases! */
148         case 6: dest [5] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x4000000;
149         case 5: dest [4] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x200000;
150         case 4: dest [3] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x10000;
151         case 3: dest [2] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0x800;
152         case 2: dest [1] = 0x80 | (wc & 0x3f); wc = wc >> 6; wc |= 0xc0;
153         case 1: dest [0] = wc;
154     }
155     return count;
156 }
157 
158 ucs4_t
utf8_read_wchar(std::istream & is)159 utf8_read_wchar (std::istream &is)
160 {
161     unsigned char utf8[6];
162     ucs4_t wc;
163     int count;
164     for (int i=0; i<6; ++i) {
165         is.read ((char*)(utf8+i), sizeof(unsigned char));
166         if ((count=utf8_mbtowc (&wc, utf8, i+1)) > 0)
167             return wc;
168         if (count == RET_ILSEQ)
169             return 0;
170     }
171     return 0;
172 }
173 
174 WideString
utf8_read_wstring(std::istream & is,ucs4_t delim,bool rm_delim)175 utf8_read_wstring (std::istream &is, ucs4_t delim, bool rm_delim)
176 {
177     WideString str;
178     ucs4_t wc;
179     while ((wc = utf8_read_wchar (is)) > 0) {
180         if (wc != delim)
181             str.push_back (wc);
182         else {
183             if (!rm_delim)
184                 str.push_back (wc);
185             break;
186         }
187     }
188     return str;
189 }
190 
191 std::ostream &
utf8_write_wchar(std::ostream & os,ucs4_t wc)192 utf8_write_wchar (std::ostream &os, ucs4_t wc)
193 {
194     unsigned char utf8[6];
195     int count = 0;
196 
197     if ((count=utf8_wctomb (utf8, wc, 6)) > 0)
198         os.write ((char*)utf8, count * sizeof (unsigned char));
199 
200     return os;
201 }
202 
203 std::ostream &
utf8_write_wstring(std::ostream & os,const WideString & wstr)204 utf8_write_wstring (std::ostream &os, const WideString & wstr)
205 {
206     for (unsigned int i=0; i<wstr.size (); ++i)
207         utf8_write_wchar (os, wstr [i]);
208 
209     return os;
210 }
211 
212 WideString
utf8_mbstowcs(const String & str)213 utf8_mbstowcs (const String & str)
214 {
215     WideString wstr;
216     ucs4_t wc;
217     unsigned int sn = 0;
218     int un = 0;
219 
220     const unsigned char *s = (const unsigned char *) str.c_str ();
221 
222     while (sn < str.length () && *s != 0 &&
223             (un=utf8_mbtowc (&wc, s, str.length () - sn)) > 0) {
224         wstr.push_back (wc);
225         s += un;
226         sn += un;
227     }
228     return wstr;
229 }
230 
231 WideString
utf8_mbstowcs(const char * str,int len)232 utf8_mbstowcs (const char *str, int len)
233 {
234     WideString wstr;
235 
236     if (str) {
237         ucs4_t wc;
238         unsigned int sn = 0;
239         int un = 0;
240 
241         if (len < 0) len = strlen (str);
242 
243         while (sn < len && *str != 0 && (un=utf8_mbtowc (&wc, (const unsigned char *)str, len - sn)) > 0) {
244             wstr.push_back (wc);
245             str += un;
246             sn += un;
247 
248         }
249     }
250     return wstr;
251 }
252 
253 String
utf8_wcstombs(const WideString & wstr)254 utf8_wcstombs (const WideString & wstr)
255 {
256     String str;
257     char utf8 [6];
258     int un = 0;
259 
260     for (unsigned int i = 0; i<wstr.size (); ++i) {
261         un = utf8_wctomb ((unsigned char*)utf8, wstr [i], 6);
262         if (un > 0)
263             str.append (utf8, un);
264     }
265     return str;
266 }
267 
268 String
utf8_wcstombs(const ucs4_t * wstr,int len)269 utf8_wcstombs (const ucs4_t *wstr, int len)
270 {
271     String str;
272     char utf8 [6];
273     int un = 0;
274 
275     if (wstr) {
276         if (len < 0)
277             for (len = 0; wstr [len]; ++len) NULL;
278 
279         for (int i = 0; i < len; ++i) {
280             un = utf8_wctomb ((unsigned char*)utf8, wstr [i], 6);
281             if (un > 0)
282                 str.append (utf8, un);
283         }
284     }
285     return str;
286 }
287 
288 String
scim_validate_locale(const String & locale)289 scim_validate_locale (const String& locale)
290 {
291     String good;
292 
293     String last = String (setlocale (LC_CTYPE, 0));
294 
295     if (setlocale (LC_CTYPE, locale.c_str ())) {
296         good = locale;
297     } else {
298         std::vector<String> vec;
299         if (scim_split_string_list (vec, locale, '.') == 2) {
300             if (isupper (vec[1][0])) {
301                 for (String::iterator i=vec[1].begin (); i!=vec[1].end (); ++i)
302                     *i = (char) tolower (*i);
303             } else {
304                 for (String::iterator i=vec[1].begin (); i!=vec[1].end (); ++i)
305                     *i = (char) toupper (*i);
306             }
307             if (setlocale (LC_CTYPE, (vec[0] + "." + vec[1]).c_str ())) {
308                 good = vec [0] + "." + vec[1];
309             }
310         }
311     }
312 
313     setlocale (LC_CTYPE, last.c_str ());
314 
315     return good;
316 }
317 
318 String
scim_get_locale_encoding(const String & locale)319 scim_get_locale_encoding (const String& locale)
320 {
321     String last = String (setlocale (LC_CTYPE, 0));
322     String encoding;
323 
324     if (setlocale (LC_CTYPE, locale.c_str ()))
325         encoding = String (nl_langinfo (CODESET));
326     else {
327         std::vector<String> vec;
328         if (scim_split_string_list (vec, locale, '.') == 2) {
329             if (isupper (vec[1][0])) {
330                 for (String::iterator i=vec[1].begin (); i!=vec[1].end (); ++i)
331                     *i = (char) tolower (*i);
332             } else {
333                 for (String::iterator i=vec[1].begin (); i!=vec[1].end (); ++i)
334                     *i = (char) toupper (*i);
335             }
336             if (setlocale (LC_CTYPE, (vec[0] + "." + vec[1]).c_str ()))
337                 encoding = String (nl_langinfo (CODESET));
338         }
339 
340     }
341 
342     setlocale (LC_CTYPE, last.c_str ());
343 
344     return encoding;
345 }
346 
347 int
scim_get_locale_maxlen(const String & locale)348 scim_get_locale_maxlen (const String& locale)
349 {
350     int maxlen;
351 
352     String last = String (setlocale (LC_CTYPE, 0));
353 
354     if (setlocale (LC_CTYPE, locale.c_str ()))
355         maxlen = MB_CUR_MAX;
356     else
357         maxlen = 1;
358 
359     setlocale (LC_CTYPE, last.c_str ());
360     return maxlen;
361 }
362 
363 int
scim_split_string_list(std::vector<String> & vec,const String & str,char delim)364 scim_split_string_list (std::vector<String>& vec, const String& str, char delim)
365 {
366     int count = 0;
367 
368     String temp;
369     String::const_iterator bg, ed;
370 
371     vec.clear ();
372 
373     bg = str.begin ();
374     ed = str.begin ();
375 
376     while (bg != str.end () && ed != str.end ()) {
377         for (; ed != str.end (); ++ed) {
378             if (*ed == delim)
379                 break;
380         }
381         temp.assign (bg, ed);
382         vec.push_back (temp);
383         ++count;
384 
385         if (ed != str.end ())
386             bg = ++ ed;
387     }
388     return count;
389 }
390 
391 String
scim_combine_string_list(const std::vector<String> & vec,char delim)392 scim_combine_string_list (const std::vector<String>& vec, char delim)
393 {
394     String result;
395     for (std::vector<String>::const_iterator i = vec.begin (); i!=vec.end (); ++i) {
396         result += *i;
397         if (i+1 != vec.end ())
398             result += delim;
399     }
400     return result;
401 }
402 
403 bool
scim_if_wchar_ucs4_equal()404 scim_if_wchar_ucs4_equal ()
405 {
406     if (sizeof (wchar_t) != sizeof (ucs4_t))
407         return false;
408 
409     iconv_t cd;
410     wchar_t wcbuf [2] = {0,0};
411     ucs4_t  ucsbuf [2] = {0x4E00, 0x0001};
412     size_t  wclen = 2 * sizeof (wchar_t);
413     size_t  ucslen = 2 * sizeof (ucs4_t);
414 
415     char *wcp = (char *) wcbuf;
416     ICONV_CONST char *ucsp = (ICONV_CONST char *) ucsbuf;
417 
418     if (scim_is_little_endian ())
419         cd = iconv_open ("UCS-4LE", "wchar_t");
420     else
421         cd = iconv_open ("UCS-4BE", "wchar_t");
422 
423     if (cd == (iconv_t) -1)
424         return false;
425 
426     iconv (cd, &ucsp, &ucslen, &wcp, &wclen);
427 
428     iconv_close (cd);
429 
430     if (wcbuf [0] == (wchar_t) ucsbuf [0] &&
431         wcbuf [1] == (wchar_t) ucsbuf [1])
432         return true;
433 
434     return false;
435 }
436 
437 static struct {
438     ucs4_t half;
439     ucs4_t full;
440     ucs4_t size;
441 } __half_full_table [] = {
442     {0x0020, 0x3000, 1},
443     {0x0021, 0xFF01, 0x5E},
444     {0x00A2, 0xFFE0, 2},
445     {0x00A5, 0xFFE5, 1},
446     {0x00A6, 0xFFE4, 1},
447     {0x00AC, 0xFFE2, 1},
448     {0x00AF, 0xFFE3, 1},
449     {0x20A9, 0xFFE6, 1},
450     {0xFF61, 0x3002, 1},
451     {0xFF62, 0x300C, 2},
452     {0xFF64, 0x3001, 1},
453     {0xFF65, 0x30FB, 1},
454     {0xFF66, 0x30F2, 1},
455     {0xFF67, 0x30A1, 1},
456     {0xFF68, 0x30A3, 1},
457     {0xFF69, 0x30A5, 1},
458     {0xFF6A, 0x30A7, 1},
459     {0xFF6B, 0x30A9, 1},
460     {0xFF6C, 0x30E3, 1},
461     {0xFF6D, 0x30E5, 1},
462     {0xFF6E, 0x30E7, 1},
463     {0xFF6F, 0x30C3, 1},
464     {0xFF70, 0x30FC, 1},
465     {0xFF71, 0x30A2, 1},
466     {0xFF72, 0x30A4, 1},
467     {0xFF73, 0x30A6, 1},
468     {0xFF74, 0x30A8, 1},
469     {0xFF75, 0x30AA, 2},
470     {0xFF77, 0x30AD, 1},
471     {0xFF78, 0x30AF, 1},
472     {0xFF79, 0x30B1, 1},
473     {0xFF7A, 0x30B3, 1},
474     {0xFF7B, 0x30B5, 1},
475     {0xFF7C, 0x30B7, 1},
476     {0xFF7D, 0x30B9, 1},
477     {0xFF7E, 0x30BB, 1},
478     {0xFF7F, 0x30BD, 1},
479     {0xFF80, 0x30BF, 1},
480     {0xFF81, 0x30C1, 1},
481     {0xFF82, 0x30C4, 1},
482     {0xFF83, 0x30C6, 1},
483     {0xFF84, 0x30C8, 1},
484     {0xFF85, 0x30CA, 6},
485     {0xFF8B, 0x30D2, 1},
486     {0xFF8C, 0x30D5, 1},
487     {0xFF8D, 0x30D8, 1},
488     {0xFF8E, 0x30DB, 1},
489     {0xFF8F, 0x30DE, 5},
490     {0xFF94, 0x30E4, 1},
491     {0xFF95, 0x30E6, 1},
492     {0xFF96, 0x30E8, 6},
493     {0xFF9C, 0x30EF, 1},
494     {0xFF9D, 0x30F3, 1},
495     {0xFFA0, 0x3164, 1},
496     {0xFFA1, 0x3131, 30},
497     {0xFFC2, 0x314F, 6},
498     {0xFFCA, 0x3155, 6},
499     {0xFFD2, 0x315B, 9},
500     {0xFFE9, 0x2190, 4},
501     {0xFFED, 0x25A0, 1},
502     {0xFFEE, 0x25CB, 1},
503     {0,0,0}
504 };
505 
506 
507 /**
508  * convert a half width unicode char to full width char
509  */
510 ucs4_t
scim_wchar_to_full_width(ucs4_t code)511 scim_wchar_to_full_width (ucs4_t code)
512 {
513     int i=0;
514     while (__half_full_table [i].size) {
515         if (code >= __half_full_table [i].half &&
516             code <  __half_full_table [i].half +
517                     __half_full_table [i].size)
518             return __half_full_table [i].full +
519                     (code - __half_full_table [i].half);
520         ++ i;
521     }
522     return code;
523 }
524 
525 /**
526  * convert a full width unicode char to half width char
527  */
528 ucs4_t
scim_wchar_to_half_width(ucs4_t code)529 scim_wchar_to_half_width (ucs4_t code)
530 {
531     int i=0;
532     while (__half_full_table [i].size) {
533         if (code >= __half_full_table [i].full &&
534             code <  __half_full_table [i].full +
535                     __half_full_table [i].size)
536             return __half_full_table [i].half +
537                     (code - __half_full_table [i].full);
538         ++ i;
539     }
540     return code;
541 }
542 
543 String
scim_get_home_dir()544 scim_get_home_dir ()
545 {
546     const char * home_dir = 0;
547 
548     struct passwd *pw;
549 
550     setpwent ();
551     pw = getpwuid (getuid ());
552     endpwent ();
553 
554     if (pw) {
555         home_dir = pw->pw_dir;
556     }
557 
558     if (!home_dir) {
559         home_dir = getenv ("HOME");
560     }
561 
562     return String (home_dir);
563 }
564 
565 String
scim_get_user_name()566 scim_get_user_name ()
567 {
568     struct passwd *pw;
569     const char *user_name;
570 
571     setpwent ();
572     pw = getpwuid (getuid ());
573     endpwent ();
574 
575     if (pw && pw->pw_name)
576         return String (pw->pw_name);
577     else if ((user_name = getenv ("USER")) != NULL)
578         return String (user_name);
579 
580     char uid_str [10];
581 
582     snprintf (uid_str, 10, "%u", getuid ());
583 
584     return String (uid_str);
585 }
586 
587 String
scim_get_user_data_dir()588 scim_get_user_data_dir ()
589 {
590     String dir = scim_get_home_dir () + String ("/.scim");
591     scim_make_dir (dir);
592     return dir;
593 }
594 
595 String
scim_get_current_locale()596 scim_get_current_locale ()
597 {
598     char *locale = setlocale (LC_MESSAGES, 0);
599 
600     if (locale) return String (locale);
601     return String ();
602 }
603 
scim_get_current_language()604 String scim_get_current_language ()
605 {
606     return scim_get_locale_language (scim_get_current_locale ());
607 }
608 
609 bool
scim_is_little_endian()610 scim_is_little_endian ()
611 {
612     short endian = 1;
613     return (*((char *)&endian) != 0);
614 }
615 
616 size_t
scim_load_file(const String & filename,char ** bufptr)617 scim_load_file (const String &filename, char **bufptr)
618 {
619     if (!filename.length ())
620         return 0;
621 
622     struct stat statbuf;
623 
624     if (stat (filename.c_str (), &statbuf) < 0 ||
625         !S_ISREG (statbuf.st_mode) ||
626         !statbuf.st_size)
627         return 0;
628 
629     if (!bufptr)
630         return statbuf.st_size;
631 
632     FILE *fp = fopen (filename.c_str (), "r");
633 
634     if (fp == NULL) {
635         *bufptr = 0;
636         return 0;
637     }
638 
639     try {
640         *bufptr = new char [statbuf.st_size];
641     } catch (...) {
642         fclose (fp);
643         throw;
644     }
645 
646     if (! (*bufptr)) {
647         fclose (fp);
648         return 0;
649     }
650 
651     size_t size = fread (*bufptr, 1, statbuf.st_size, fp);
652 
653     fclose (fp);
654 
655     if (!size) {
656         delete [] *bufptr;
657         *bufptr = 0;
658     }
659 
660     return size;
661 }
662 
663 bool
scim_make_dir(const String & dir)664 scim_make_dir (const String &dir)
665 {
666     std::vector <String> paths;
667     String path;
668 
669     scim_split_string_list (paths, dir, SCIM_PATH_DELIM);
670 
671     for (size_t i = 0; i < paths.size (); ++i) {
672         path += SCIM_PATH_DELIM_STRING + paths [i];
673 
674         //Make the dir if it's not exist.
675         if (access (path.c_str (), R_OK) != 0) {
676             mkdir (path.c_str (), S_IRUSR | S_IWUSR | S_IXUSR);
677             if (access (path.c_str (), R_OK) != 0)
678                 return false;
679         }
680     }
681     return true;
682 }
683 
684 struct __Language {
685     const char *code;
686     const char *normalized;
687     const char *name;
688     const char *untranslated;
689     const char *locale_suffix;
690 };
691 
692 static __Language __languages [] = {
693     { "C",        NULL, N_("English/Keyboard"), NULL, NULL},
694     { "am_ET",    NULL, N_("Amharic"), NULL, NULL },
695     { "ar",    "ar_EG", N_("Arabic"), NULL, NULL },
696     { "ar_EG",    NULL, N_("Arabic (Egypt)"), NULL, NULL },
697     { "ar_LB",    NULL, N_("Arabic (Lebanon)"), NULL, NULL },
698     { "as_IN",    NULL, N_("Assamese"), NULL, NULL},
699     { "az_AZ",    NULL, N_("Azerbaijani"), NULL, NULL },
700     { "be_BY",    NULL, N_("Belarusian"), "Беларуская мова", NULL },
701     { "bg_BG",    NULL, N_("Bulgarian"), "Български", NULL },
702     { "bn",    "bn_BD", N_("Bengali"), "বাংলা", NULL },
703     { "bn_BD",    NULL, N_("Bengali"), "বাংলা", NULL },
704     { "bn_IN",    NULL, N_("Bengali (India)"), "বাংলা", NULL },
705     { "bo",       NULL, N_("Tibetan"), NULL, NULL },
706     { "bs_BA",    NULL, N_("Bosnian"), NULL, NULL },
707     { "ca_ES",    NULL, N_("Catalan"), "Català", "@euro" },
708     { "cs_CZ",    NULL, N_("Czech"), "čeština", NULL },
709     { "cy_GB",    NULL, N_("Welsh"), "Cymraeg", NULL },
710     { "da_DK",    NULL, N_("Danish"), "dansk", "@euro" },
711     { "de_DE",    NULL, N_("German"), "Deutsch", "@euro" },
712     { "dv_MV",    NULL, N_("Divehi"), "ދިވެހިބަސް", NULL },
713     { "el_GR",    NULL, N_("Greek"), "ελληνικά", NULL },
714     { "en"   , "en_US", N_("English"), "English", NULL },
715     { "en_AU",    NULL, N_("English (Australian)"), "Australian English", NULL },
716     { "en_CA",    NULL, N_("English (Canadian)"), "Canadian English", NULL },
717     { "en_GB",    NULL, N_("English (British)"), "British English", ".iso885915" },
718     { "en_IE",    NULL, N_("English (Ireland)"), "Irish English", NULL },
719     { "en_US",    NULL, N_("English (American)"), "American English", ".iso885915" },
720     { "eo",       NULL, N_("Esperanto"), "Esperanto", NULL },
721     { "es",    "es_ES", N_("Spanish"), "Español", NULL },
722     { "es_ES",    NULL, N_("Spanish"), "Español", "@euro" },
723     { "es_MX",    NULL, N_("Spanish (Mexico)"), "Español (Mexico)", NULL },
724     { "et_EE",    NULL, N_("Estonian"), "Eesti", ".iso885915" },
725     { "eu_ES",    NULL, N_("Basque"), "Euskara", "@euro" },
726     { "fa_IR",    NULL, N_("Persian"), "فارسی", NULL },
727     { "fi_FI",    NULL, N_("Finnish"), "Suomi", "@euro" },
728     { "fr_FR",    NULL, N_("French"), "Français", "@euro" },
729     { "ga_IE",    NULL, N_("Irish"), "Gaeilge", "@euro" },
730     { "gl_ES",    NULL, N_("Galician"), "Galego", "@euro" },
731     { "gu_IN",    NULL, N_("Gujarati"), NULL, NULL },
732     { "he_IL",    NULL, N_("Hebrew"), "עברית", NULL },
733     { "hi_IN",    NULL, N_("Hindi"), "हिंदी", NULL },
734     { "hr_HR",    NULL, N_("Croatian"), "Hrvatski", NULL },
735     { "hu_HU",    NULL, N_("Hungarian"), "Magyar", NULL },
736     { "hy_AM",    NULL, N_("Armenian"), "Հայերէն", NULL },
737     { "ia"   ,    NULL, N_("Interlingua"), NULL },
738     { "id_ID",    NULL, N_("Indonesian"), "Bahasa Indonesia", NULL },
739     { "is_IS",    NULL, N_("Icelandic"), NULL, NULL },
740     { "it_IT",    NULL, N_("Italian"), "Italiano", "@euro" },
741     { "iw_IL",    NULL, N_("Hebrew"), "עברית", NULL },
742     { "ja_JP",    NULL, N_("Japanese"), "日本語", ".EUC-JP,.SJIS,.eucJP" },
743     { "ka_GE",    NULL, N_("Georgian"), "ქართული", NULL },
744     { "kk_KZ",    NULL, N_("Kazakh"), NULL, NULL },
745     { "km",       NULL, N_("Cambodian"), NULL, NULL },
746     { "kn_IN",    NULL, N_("Kannada"), "ಕನ್ನಡ", NULL },
747     { "ko_KR",    NULL, N_("Korean"), "한국어", ".EUC-KR,.eucKR" },
748     { "lo_LA",    NULL, N_("Laothian"), NULL, NULL },
749     { "lt_LT",    NULL, N_("Lithuanian"), "Lietuvių", NULL },
750     { "lv_LV",    NULL, N_("Latvian"), "Latviešu", NULL },
751     { "mk_MK",    NULL, N_("Macedonian"), NULL, NULL },
752     { "ml_IN",    NULL, N_("Malayalam"), "മലയാളം", NULL },
753     { "mn_MN",    NULL, N_("Mongolian"), "Монгол", NULL },
754     { "mr_IN",    NULL, N_("Marathi"), NULL, NULL },
755     { "ms_MY",    NULL, N_("Malay"), "Bahasa Melayu", NULL },
756     { "my_MM",    NULL, N_("Burmese"), "", NULL },
757     { "ne_NP",    NULL, N_("Nepali"), NULL, NULL },
758     { "nl_NL",    NULL, N_("Dutch"), "Nederlands", "@euro" },
759     { "nn_NO",    NULL, N_("Norwegian (nynorsk)"), "Norsk (nynorsk)", NULL },
760     { "no_NO",    NULL, N_("Norwegian (bokmal)"), "Norsk (bokmål)", NULL },
761     { "or_IN",    NULL, N_("Oriya"), NULL, NULL },
762     { "pa_IN",    NULL, N_("Punjabi"), NULL, NULL },
763     { "pl_PL",    NULL, N_("Polish"), "Polski", NULL },
764     { "pt",    "pt_PT", N_("Portuguese"), "Português", NULL },
765     { "pt_BR",    NULL, N_("Portuguese (Brazil)"), "Português do Brasil", NULL },
766     { "pt_PT",    NULL, N_("Portuguese"), "Português", "@euro" },
767     { "ro_RO",    NULL, N_("Romanian"), "Română", NULL },
768     { "ru_RU",    NULL, N_("Russian"), "русский", ".koi8r" },
769     { "si_LK",    NULL, N_("Sinhala"), "සිංහල", NULL },
770     { "sk_SK",    NULL, N_("Slovak"), "Slovenský", NULL },
771     { "sl_SI",    NULL, N_("Slovenian"), "Slovenščina", NULL },
772     { "sq_AL",    NULL, N_("Albanian"), "Shqip", NULL },
773     { "sr",    "sr_YU", N_("Serbian"), "српски", NULL },
774     { "sr_CS",    NULL, N_("Serbian"), "српски", NULL },
775     { "sr_YU",    NULL, N_("Serbian"), "српски", "@cyrillic" },
776     { "sv",    "sv_SE", N_("Swedish"), "Svenska", NULL },
777     { "sv_FI",    NULL, N_("Swedish (Finland)"), "Svenska (Finland)", "@euro" },
778     { "sv_SE",    NULL, N_("Swedish"), "Svenska", ".iso885915" },
779     { "ta_IN",    NULL, N_("Tamil"), NULL, NULL },
780     { "te_IN",    NULL, N_("Telugu"), NULL, NULL },
781     { "th_TH",    NULL, N_("Thai"), "ไทย", NULL },
782     { "tr_TR",    NULL, N_("Turkish"), "Türkçe", NULL },
783     { "ug",       NULL, N_("Uighur"), NULL, NULL },
784     { "uk_UA",    NULL, N_("Ukrainian"), "Українська", NULL },
785     { "ur_PK",    NULL, N_("Urdu"), NULL, NULL },
786     { "uz_UZ",    NULL, N_("Uzbek"), NULL, "@cyrillic" },
787     { "vi_VN",    NULL, N_("Vietnamese"), "Việt Nam", ".tcvn" },
788     { "wa_BE",    NULL, N_("Walloon"), "Walon", "@euro" },
789     { "yi"   , "yi_US", N_("Yiddish"), "ייִדיש", NULL },
790     { "yi_US",    NULL, N_("Yiddish"), "ייִדיש", NULL },
791     { "zh",    "zh_CN", N_("Chinese"), "中文", NULL },
792     { "zh_CN",    NULL, N_("Chinese (simplified)"), "中文 (简体)", ".GB18030,.GBK,.GB2312,.eucCN" },
793     { "zh_HK", "zh_TW", N_("Chinese (traditional)"), "中文 (繁體)", NULL },
794     { "zh_SG", "zh_CN", N_("Chinese (simplified)"), "中文 (简体)", ".GBK" },
795     { "zh_TW",    NULL, N_("Chinese (traditional)"), "中文 (繁體)", ".eucTW" },
796     { "", "", "", NULL, NULL }
797 };
798 
799 class __LanguageLess
800 {
801 public:
operator ()(const __Language & lhs,const __Language & rhs) const802     bool operator () (const __Language &lhs, const __Language &rhs) const {
803         return strcmp (lhs.code, rhs.code) < 0;
804     }
805 
operator ()(const __Language & lhs,const String & rhs) const806     bool operator () (const __Language &lhs, const String &rhs) const {
807         return strcmp (lhs.code, rhs.c_str ()) < 0;
808     }
809 
operator ()(const String & lhs,const __Language & rhs) const810     bool operator () (const String &lhs, const __Language &rhs) const {
811         return strcmp (lhs.c_str (), rhs.code) < 0;
812     }
813 };
814 
815 static __Language *
__find_language(const String & lang)816 __find_language (const String &lang)
817 {
818     static __Language *langs_begin = __languages;
819     static __Language *langs_end   = __languages + sizeof (__languages) / sizeof (__Language) - 1;
820 
821     String nlang = lang;
822     bool contry_code = false;
823 
824     // Normalize the language name.
825     for (String::iterator it = nlang.begin (); it != nlang.end (); ++it) {
826         if (*it == '-' || *it == '_') {
827             *it = '_';
828             contry_code = true;
829         } else if (contry_code) {
830             *it = toupper (*it);
831         } else {
832             *it = tolower (*it);
833         }
834     }
835 
836     __Language *result = std::lower_bound (langs_begin, langs_end, nlang, __LanguageLess ());
837 
838     if (result != langs_end) {
839         if (strncmp (result->code, nlang.c_str (), strlen (result->code)) == 0 ||
840             (strncmp (result->code, nlang.c_str (), nlang.length ()) == 0 &&
841              strncmp (result->code, (result+1)->code, nlang.length ()) != 0))
842             return result;
843     }
844 
845     return NULL;
846 }
847 
848 String
scim_get_language_name(const String & lang)849 scim_get_language_name (const String &lang)
850 {
851     return String (_(scim_get_language_name_english (lang).c_str ()));
852 }
853 
854 String
scim_get_language_name_english(const String & lang)855 scim_get_language_name_english (const String &lang)
856 {
857     __Language *result = __find_language (lang);
858 
859     if (result)
860         return String (result->name);
861 
862     return String ("Other");
863 }
864 
865 String
scim_get_language_name_untranslated(const String & lang)866 scim_get_language_name_untranslated (const String &lang)
867 {
868     __Language *result = __find_language (lang);
869 
870     if (result) {
871         if (result->untranslated)
872             return String (result->untranslated);
873         else
874             return String (_(result->name));
875     }
876 
877     return String (_("Other"));
878 }
879 
880 String
scim_get_language_locales(const String & lang)881 scim_get_language_locales (const String &lang)
882 {
883     __Language *result = __find_language (lang);
884 
885     std::vector<String> locales;
886 
887     if (result) {
888         String good;
889 
890         if (strlen (result->code) < 5 && result->normalized)
891             result = __find_language (result->normalized);
892 
893         good = scim_validate_locale (String (result->code) + ".UTF-8");
894 
895         if (good.length ()) locales.push_back (good);
896 
897         if (result->locale_suffix) {
898             std::vector<String> suffixes;
899 
900             scim_split_string_list (suffixes, result->locale_suffix, ',');
901             for (size_t i = 0; i < suffixes.size (); ++ i) {
902                 good = scim_validate_locale (String (result->code) + suffixes [i]);
903                 if (good.length ()) locales.push_back (good);
904             }
905         }
906 
907         good = scim_validate_locale (result->code);
908 
909         if (good.length ()) locales.push_back (good);
910     }
911 
912     return scim_combine_string_list (locales, ',');
913 }
914 
915 String
scim_get_locale_language(const String & locale)916 scim_get_locale_language (const String &locale)
917 {
918     if (locale.length () == 0) return String ();
919 
920     String str = locale.substr (0, locale.find ('.'));
921     return scim_validate_language (str.substr (0, str.find ('@')));
922 }
923 
924 String
scim_validate_language(const String & lang)925 scim_validate_language (const String &lang)
926 {
927     __Language *result = __find_language (lang);
928 
929     if (result)
930         return String (result->code);
931 
932     // Add prefix ~ to let other become the last item when sorting.
933     return String ("~other");
934 }
935 
936 String
scim_get_normalized_language(const String & lang)937 scim_get_normalized_language (const String &lang)
938 {
939     __Language *result = __find_language (lang);
940 
941     if (result) {
942         if (result->normalized) return String (result->normalized);
943         else return String (result->code);
944     }
945 
946     // Add prefix ~ to let other become the last item when sorting.
947     return String ("~other");
948 }
949 
950 #ifndef SCIM_LAUNCHER
951  #define SCIM_LAUNCHER  (SCIM_LIBEXECDIR "/scim-launcher")
952 #endif
953 
scim_launch(bool daemon,const String & config,const String & imengines,const String & frontend,char * const argv[])954 int  scim_launch (bool          daemon,
955                   const String &config,
956                   const String &imengines,
957                   const String &frontend,
958                   char  * const argv [])
959 {
960     if (!config.length () || !imengines.length () || !frontend.length ())
961         return -1;
962 
963     int   new_argc = 0;
964     char *new_argv [40];
965     char verbose_buf [10];
966 
967     new_argv [new_argc ++] = strdup (SCIM_LAUNCHER);
968 
969     if (daemon)
970         new_argv [new_argc ++] = strdup ("-d");
971 
972     new_argv [new_argc ++] = strdup ("-c");
973     new_argv [new_argc ++] = strdup (config.c_str ());
974     new_argv [new_argc ++] = strdup ("-e");
975     new_argv [new_argc ++] = strdup (imengines.c_str ());
976     new_argv [new_argc ++] = strdup ("-f");
977     new_argv [new_argc ++] = strdup (frontend.c_str ());
978 
979     if (argv) {
980         for (int i = 0; argv [i] && new_argc < 40 ; ++i, ++new_argc)
981             new_argv [new_argc] = strdup (argv [i]);
982     }
983 
984     new_argv [new_argc] = 0;
985 
986     pid_t child_pid;
987 
988     child_pid = fork ();
989 
990     // Error fork.
991     if (child_pid < 0) return -1;
992 
993     // In child process, start scim-launcher.
994     if (child_pid == 0) {
995         return execv (SCIM_LAUNCHER, new_argv);
996     }
997 
998     // In parent process, wait the child exit.
999 
1000     for (int i = 0; i < new_argc; ++i)
1001         if (new_argv [i]) free (new_argv [i]);
1002 
1003     int status;
1004     pid_t ret_pid;
1005 
1006     ret_pid = waitpid (child_pid, &status, 0);
1007 
1008     if (ret_pid == child_pid && WIFEXITED(status))
1009         return WEXITSTATUS(status);
1010 
1011     return -1;
1012 }
1013 
1014 #ifndef SCIM_PANEL_PROGRAM
1015   #define SCIM_PANEL_PROGRAM  (SCIM_LIBEXECDIR "/scim-panel-gtk")
1016 #endif
1017 
scim_launch_panel(bool daemon,const String & config,const String & display,char * const argv[])1018 int scim_launch_panel (bool          daemon,
1019                        const String &config,
1020                        const String &display,
1021                        char * const  argv [])
1022 {
1023     if (!config.length ())
1024         return -1;
1025 
1026     String panel_program = scim_global_config_read (SCIM_GLOBAL_CONFIG_DEFAULT_PANEL_PROGRAM, String (SCIM_PANEL_PROGRAM));
1027 
1028     if (!panel_program.length ())
1029         panel_program = String (SCIM_PANEL_PROGRAM);
1030 
1031     if (panel_program [0] != SCIM_PATH_DELIM) {
1032         panel_program = String (SCIM_LIBEXECDIR) +
1033                         String (SCIM_PATH_DELIM_STRING) +
1034                         panel_program;
1035     }
1036 
1037     //if the file is not exist or is not executable, fallback to default
1038     if (access (panel_program.c_str (), X_OK) != 0)
1039             panel_program = String (SCIM_PANEL_PROGRAM);
1040 
1041     int   new_argc = 0;
1042     char *new_argv [80];
1043 
1044     new_argv [new_argc ++] = strdup (panel_program.c_str ());
1045 
1046     new_argv [new_argc ++] = strdup ("--display");
1047     new_argv [new_argc ++] = strdup (display.c_str ());
1048 
1049     new_argv [new_argc ++] = strdup ("-c");
1050     new_argv [new_argc ++] = strdup (config.c_str ());
1051 
1052     if (daemon)
1053         new_argv [new_argc ++] = strdup ("-d");
1054 
1055     if (argv) {
1056         for (int i = 0; argv [i] && new_argc < 40; ++i, ++new_argc)
1057             new_argv [new_argc] = strdup (argv [i]);
1058     }
1059 
1060     new_argv [new_argc] = 0;
1061 
1062     pid_t child_pid;
1063 
1064     child_pid = fork ();
1065 
1066     // Error fork.
1067     if (child_pid < 0) return -1;
1068 
1069     // In child process, start scim-launcher.
1070     if (child_pid == 0) {
1071         return execv (panel_program.c_str (), new_argv);
1072     }
1073 
1074     // In parent process, wait the child exit.
1075 
1076     for (int i = 0; i < new_argc; ++i)
1077         if (new_argv [i]) free (new_argv [i]);
1078 
1079     int status;
1080     pid_t ret_pid;
1081 
1082     ret_pid = waitpid (child_pid, &status, 0);
1083 
1084     if (ret_pid == child_pid && WIFEXITED(status))
1085         return WEXITSTATUS(status);
1086 
1087     return -1;
1088 }
1089 
1090 void
scim_usleep(unsigned int usec)1091 scim_usleep (unsigned int usec)
1092 {
1093     if (usec == 0) return;
1094 
1095 #if HAVE_NANOSLEEP
1096     struct timespec req, rem;
1097 
1098     req.tv_sec = usec / 1000000;
1099     req.tv_nsec = (usec % 1000000) * 1000;
1100 
1101     while (nanosleep (&req, &rem) == -1 && errno == EINTR && (rem.tv_sec != 0 || rem.tv_nsec != 0))
1102         req = rem;
1103 #elif HAVE_USLEEP
1104     unsigned int sec = usec / 1000000;
1105     usec %= 1000000;
1106 
1107     for (unsigned int i = 0; i < sec; ++i)
1108         sleep (1);
1109 
1110     usleep (usec);
1111 #else
1112     unsigned int sec = usec / 1000000;
1113 
1114     sleep (sec ? sec : 1);
1115 #endif
1116 }
1117 
scim_daemon()1118 void scim_daemon ()
1119 {
1120 #if HAVE_DAEMON
1121     if (daemon (0, 0) == -1)
1122         std::cerr << "Error to make SCIM into a daemon!\n";
1123 
1124     return;
1125 #else
1126     pid_t id;
1127 
1128     id = fork ();
1129     if (id == -1) {
1130         std::cerr << "Error to make SCIM into a daemon!\n";
1131         return;
1132     } else if (id > 0) {
1133         _exit (0);
1134     }
1135 
1136     id = fork ();
1137     if (id == -1) {
1138         std::cerr << "Error to make SCIM into a daemon!\n";
1139         return;
1140     } else if (id > 0) {
1141         _exit (0);
1142     }
1143 
1144     return;
1145 #endif
1146 }
1147 
1148 } // namespace scim
1149 
1150 /*
1151 vi:ts=4:ai:nowrap:expandtab
1152 */
1153