1 /** @file scim_special_table.cpp
2  * implementation of SpecialTable class.
3  */
4 
5 /*
6  * Smart Pinyin Input Method
7  *
8  * Copyright (c) 2005 James Su <suzhe@tsinghua.org.cn>
9  *
10  * $Id: scim_special_table.cpp,v 1.1.1.1 2005/01/06 13:31:02 suzhe Exp $
11  *
12  */
13 
14 #define Uses_STL_FUNCTIONAL
15 #define Uses_STL_VECTOR
16 #define Uses_STL_IOSTREAM
17 #define Uses_STL_ALGORITHM
18 #define Uses_STL_MAP
19 #define Uses_STL_UTILITY
20 #define Uses_STL_IOMANIP
21 #define Uscs_C_STRING
22 
23 #include <cstring>
24 #include <ctime>
25 
26 #include <scim.h>
27 #include "scim_pinyin_private.h"
28 #include "scim_special_table.h"
29 
30 static inline String
_trim_blank(const String & str)31 _trim_blank (const String &str)
32 {
33 	String::size_type begin, len;
34 
35 	begin = str.find_first_not_of (" \t\n\v");
36 
37 	if (begin == String::npos)
38 		return String ();
39 
40 	len = str.find_last_not_of (" \t\n\v");
41 
42 	if (len != String::npos)
43 		len = len - begin + 1;
44 
45 	return str.substr (begin, len);
46 }
47 
48 static inline String
_get_param_portion(const String & str,const String & delim="=")49 _get_param_portion (const String &str, const String &delim = "=")
50 {
51 	String ret = str;
52 	String::size_type pos = ret.find_first_of (String (" \t\v") + delim);
53 
54 	if (pos != String::npos)
55 		ret.erase (pos, String::npos);
56 
57 	return ret;
58 }
59 
60 static inline String
_get_value_portion(const String & str,const String & delim="=")61 _get_value_portion (const String &str, const String &delim = "=")
62 {
63 	String ret = str;
64 	String::size_type pos;
65 
66 	pos = ret.find_first_of (delim);
67 
68 	if (pos != String::npos)
69 		ret.erase (0, pos + 1);
70 
71 	pos = ret.find_first_not_of(" \t\v");
72 
73 	if (pos != String::npos)
74 		ret.erase (0, pos);
75 
76 	pos = ret.find_last_not_of(" \t\v");
77 
78 	if (pos != String::npos)
79 		ret.erase (pos + 1, String::npos);
80 
81 	return ret;
82 }
83 
84 static inline String
_get_line(std::istream & is)85 _get_line (std::istream &is)
86 {
87 	char temp [1024];
88 	String res;
89 
90 	while (1) {
91 		is.getline (temp, 1023);
92 		res = _trim_blank (String (temp));
93 
94 		if (res.length () > 0 && res [0] != '#') return res;
95 		if (is.eof ()) return String ();
96 	}
97 }
98 
99 static inline WideString
_hex_to_wide_string(const String & str)100 _hex_to_wide_string (const String &str)
101 {
102 	WideString ret;
103 	uint32 i = 0;
104 
105 	while (i <= str.length () - 6 && str [i] == '0' && tolower (str [i+1]) == 'x') {
106 		ucs4_t wc = (ucs4_t) strtol (str.substr (i+2, 4).c_str (), NULL, 16);
107 
108 		if (wc)
109 			ret.push_back (wc);
110 
111 		i += 6;
112 	}
113 	return ret;
114 }
115 
116 class SpecialKeyItemLessThanByKey
117 	: public std::binary_function <
118 				std::pair <String, String>,
119 				std::pair <String, String>,
120 				bool>
121 {
122 public:
operator ()(const std::pair<String,String> & lhs,const std::pair<String,String> & rhs) const123 	bool operator () (const std::pair <String, String> & lhs,
124 					  const std::pair <String, String> & rhs) const {
125 		int eq = std::strncmp (lhs.first.c_str (), rhs.first.c_str (), std::min (lhs.first.length(), rhs.first.length()));
126 
127 		if (eq < 0) return true;
128 		if (eq > 0) return false;
129 		return lhs.first.length () < rhs.first.length ();
130 	}
131 };
132 
133 class SpecialKeyItemLessThanByKeyStrictLength
134 	: public std::binary_function <
135 				std::pair <String, String>,
136 				std::pair <String, String>,
137 				bool>
138 {
139 	size_t m_min_len;
140 public:
SpecialKeyItemLessThanByKeyStrictLength(size_t min_len)141 	SpecialKeyItemLessThanByKeyStrictLength (size_t min_len) : m_min_len (min_len) {}
142 
operator ()(const std::pair<String,String> & lhs,const std::pair<String,String> & rhs) const143 	bool operator () (const std::pair <String, String> & lhs,
144 					  const std::pair <String, String> & rhs) const {
145 
146 		int eq = std::strncmp (lhs.first.c_str (), rhs.first.c_str (), std::min (lhs.first.length(), rhs.first.length()));
147 
148 		if (eq < 0) return true;
149 		if (eq > 0) return false;
150 
151 		if (lhs.first.length () >= rhs.first.length ()) return false;
152 
153 		if (lhs.first.length () < m_min_len) return true;
154 
155 		return false;
156 	}
157 };
158 
SpecialTable()159 SpecialTable::SpecialTable ()
160 	: m_max_key_length (0)
161 {
162 }
163 
SpecialTable(std::istream & is)164 SpecialTable::SpecialTable (std::istream &is)
165 	: m_max_key_length (0)
166 {
167 	load (is);
168 }
169 
170 void
load(std::istream & is)171 SpecialTable::load (std::istream &is)
172 {
173 	String line;
174 	String key;
175 	String entry;
176 	std::vector<String> items;
177 	int count = 0;
178 
179 	while (1) {
180 		line = _get_line (is);
181 
182 		if (!line.length ()) break;
183 
184 		key = _get_param_portion (line);
185 		entry = _get_value_portion (line);
186 
187 		if (!key.length () || !entry.length ()) break;
188 
189 		scim_split_string_list (items, entry, ',');
190 
191 		for (std::vector<String>::iterator it = items.begin (); it != items.end (); ++it) {
192 			if (it->length ()) {
193 				m_special_map.push_back (std::make_pair (key, *it));
194 				count ++;
195 				if (key.length () > m_max_key_length)
196 					m_max_key_length = key.length ();
197 			}
198 		}
199 	}
200 
201 	std::sort (m_special_map.begin (), m_special_map.end ());
202 
203 	m_special_map.erase (std::unique (m_special_map.begin (), m_special_map.end ()), m_special_map.end ());
204 
205 	std::stable_sort (m_special_map.begin (), m_special_map.end (), SpecialKeyItemLessThanByKey ());
206 }
207 
208 void
clear(void)209 SpecialTable::clear (void)
210 {
211 	SpecialMap ().swap (m_special_map);
212 	m_max_key_length = 0;
213 }
214 
215 bool
valid(void) const216 SpecialTable::valid (void) const
217 {
218 	return m_special_map.size () != 0;
219 }
220 
221 int
get_max_key_length(void) const222 SpecialTable::get_max_key_length (void) const
223 {
224 	return m_max_key_length;
225 }
226 
227 
228 int
find(std::vector<WideString> & result,const String & key) const229 SpecialTable::find (std::vector <WideString> & result,
230 					const String			 & key) const
231 {
232 	SpecialMap::const_iterator lower_it =
233 		std::lower_bound (m_special_map.begin (),
234 						  m_special_map.end (),
235 						  std::make_pair (key, String ()),
236 						  SpecialKeyItemLessThanByKeyStrictLength (
237 							std::max (key.length (), (size_t)3)));
238 
239 	SpecialMap::const_iterator upper_it =
240 		std::upper_bound (m_special_map.begin (),
241 						  m_special_map.end (),
242 						  std::make_pair (key, String ()),
243 						  SpecialKeyItemLessThanByKeyStrictLength (
244 							std::max (key.length (), (size_t)3)));
245 
246 	result.clear ();
247 
248 	for (SpecialMap::const_iterator it = lower_it; it != upper_it; ++ it)
249 		result.push_back (translate (it->second));
250 
251 	std::sort (result.begin (), result.end ());
252 
253 	result.erase (std::unique (result.begin (), result.end ()), result.end ());
254 
255 	return (int) result.size ();
256 }
257 
258 WideString
translate(const String & str) const259 SpecialTable::translate (const String & str) const
260 {
261 	if (str.length () > 2 && str [0] == 'X' && str [1] == '_') {
262 		if (str.length () > 7 && str [2] == 'D' &&
263 			str [3] == 'A' && str [4] == 'T' &&
264 			str [5] == 'E' && str [6] == '_')
265 			return get_date (str [7] - '1');
266 		else if (str.length () > 7 && str [2] == 'T' &&
267 			str [3] == 'I' && str [4] == 'M' &&
268 			str [5] == 'E' && str [6] == '_')
269 			return get_time (str [7] - '1');
270 		else if (str.length () > 6 && str [2] == 'D' &&
271 			str [3] == 'A' && str [4] == 'Y' && str [5] == '_')
272 			return get_day (str [6] - '1');
273 	} else if (str.length () > 5 && str [0] == '0' && (str [1] == 'x' || str [1] == 'X')) {
274 		return _hex_to_wide_string (str);
275 	}
276 
277 	return utf8_mbstowcs (str);
278 }
279 
280 static const char * __chinese_number_little_simp [] =
281 {
282 	"〇", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十", NULL
283 };
284 
285 static const char * __chinese_number_little_trad [] =
286 {
287 	"零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十", NULL
288 };
289 
290 static const char * __chinese_number_big_simp [] =
291 {
292 	"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖", "拾", NULL
293 };
294 
295 static const char * __chinese_number_big_trad [] =
296 {
297 	"零", "壹", "貳", "參", "肆", "伍", "陸", "柒", "捌", "玖", "拾", NULL
298 };
299 
300 static const char * __chinese_week_1 [] =
301 {
302 	"日", "一", "二", "三", "四", "五", "六", NULL
303 };
304 
305 static const char * __chinese_week_2 [] =
306 {
307 	"天", "一", "二", "三", "四", "五", "六", NULL
308 };
309 
310 static void
get_broken_down_time(struct tm & buf)311 get_broken_down_time (struct tm &buf)
312 {
313 	time_t simple_time = std::time (NULL);
314 
315 #ifdef HAVE_LOCALTIME_R
316 	localtime_r (&simple_time, &buf);
317 #else
318 	struct tm *tmp = localtime (&simple_time);
319 	if (tmp) memcpy (&buf, tmp, sizeof (struct tm));
320 #endif
321 }
322 
323 WideString
get_date(int type) const324 SpecialTable::get_date (int type) const
325 {
326 	struct tm cur_time;
327 	String result;
328 
329 	get_broken_down_time (cur_time);
330 
331 	cur_time.tm_year += 1900;
332 	cur_time.tm_year %= 10000;
333 	cur_time.tm_mon ++;
334 
335 	if (type == 0) {
336 		char buf [80];
337 		snprintf (buf, 80, "%d年%d月%d日",
338 			cur_time.tm_year, cur_time.tm_mon, cur_time.tm_mday);
339 		result = String (buf);
340 	} else if (type <= 4) {
341 		const char ** numbers;
342 		switch (type) {
343 			case 1: numbers = __chinese_number_little_simp; break;
344 			case 2: numbers = __chinese_number_little_trad; break;
345 			case 3: numbers = __chinese_number_big_simp; break;
346 			case 4: numbers = __chinese_number_big_trad; break;
347 		}
348 
349 		result = String (numbers [cur_time.tm_year / 1000]);
350 		cur_time.tm_year %= 1000;
351 		result += String (numbers [cur_time.tm_year / 100]);
352 		cur_time.tm_year %= 100;
353 		result += String (numbers [cur_time.tm_year / 10]);
354 		cur_time.tm_year %= 10;
355 		result += String (numbers [cur_time.tm_year]);
356 		result += String ("年");
357 
358 		if (cur_time.tm_mon < 10)
359 			result += String (numbers [cur_time.tm_mon]);
360 		else {
361 			result += String (numbers [10]);
362 			if (cur_time.tm_mon > 10)
363 				result += String (numbers [cur_time.tm_mon % 10]);
364 		}
365 		result += String ("月");
366 
367 		if (cur_time.tm_mday < 10)
368 			result += String (numbers [cur_time.tm_mday]);
369 		else {
370 			if (cur_time.tm_mday >= 20)
371 				result += String (numbers [cur_time.tm_mday / 10]);
372 			result += String (numbers [10]);
373 			if (cur_time.tm_mday % 10)
374 				result += String (numbers [cur_time.tm_mday % 10]);
375 		}
376 		result += String ("日");
377 	} else {
378 		char buf [80];
379 		snprintf (buf, 80, "%d-%d-%d",
380 			cur_time.tm_year, cur_time.tm_mon, cur_time.tm_mday);
381 		result = String (buf);
382 	}
383 	return utf8_mbstowcs (result);
384 }
385 
386 WideString
get_day(int type) const387 SpecialTable::get_day  (int type) const
388 {
389 	struct tm cur_time;
390 
391 	get_broken_down_time (cur_time);
392 
393 	switch (type) {
394 		case 1:
395 			return utf8_mbstowcs (String ("星期") + String (__chinese_week_2 [cur_time.tm_wday]));
396 		case 2:
397 			return utf8_mbstowcs (String ("礼拜") + String (__chinese_week_1 [cur_time.tm_wday]));
398 		case 3:
399 			return utf8_mbstowcs (String ("礼拜") + String (__chinese_week_2 [cur_time.tm_wday]));
400 		default:
401 			return utf8_mbstowcs (String ("星期") + String (__chinese_week_1 [cur_time.tm_wday]));
402 	}
403 }
404 
405 WideString
get_time(int type) const406 SpecialTable::get_time (int type) const
407 {
408 	struct tm cur_time;
409 	String result;
410 	char buf [80];
411 	const char ** numbers;
412 
413 	get_broken_down_time (cur_time);
414 
415 	switch (type) {
416 		case 0:
417 		case 1:
418 			snprintf (buf, 80, "%d%s%d分",
419 				cur_time.tm_hour, ((type == 0) ? "点" : "點"), cur_time.tm_min);
420 			result = String (buf);
421 			break;
422 		case 2:
423 		case 3:
424 			snprintf (buf, 80, "%s%d%s%d分",
425 				(cur_time.tm_hour <= 12) ? "上午" : "下午",
426 				(cur_time.tm_hour <= 12) ? cur_time.tm_hour : (cur_time.tm_hour - 12),
427 				((type == 2) ? "点" : "點"),
428 				cur_time.tm_min);
429 			result = String (buf);
430 			break;
431 		case 4:
432 		case 5:
433 			numbers = (type == 4) ? __chinese_number_little_simp : __chinese_number_little_trad;
434 
435 			if (cur_time.tm_hour < 10)
436 				result += String (numbers [cur_time.tm_hour]);
437 			else {
438 				if (cur_time.tm_hour >= 20)
439 					result += String (numbers [cur_time.tm_hour / 10]);
440 				result += String (numbers [10]);
441 				if (cur_time.tm_hour % 10)
442 					result += String (numbers [cur_time.tm_hour % 10]);
443 			}
444 
445 			result += String ((type == 4) ? "点" : "點");
446 
447 			if (cur_time.tm_min < 10)
448 				result += String (numbers [cur_time.tm_min]);
449 			else {
450 				if (cur_time.tm_min >= 20)
451 					result += String (numbers [cur_time.tm_min / 10]);
452 				result += String (numbers [10]);
453 				if (cur_time.tm_min % 10)
454 					result += String (numbers [cur_time.tm_min % 10]);
455 			}
456 
457 			result += String ("分");
458 			break;
459 		case 6:
460 		case 7:
461 			numbers = (type == 4) ? __chinese_number_little_simp : __chinese_number_little_trad;
462 
463 			if (cur_time.tm_hour <= 12)
464 				result += String ("上午");
465 			else {
466 				result += String ("下午");
467 				cur_time.tm_hour -= 12;
468 			}
469 
470 			if (cur_time.tm_hour < 10)
471 				result += String (numbers [cur_time.tm_hour]);
472 			else {
473 				result += String (numbers [10]);
474 				if (cur_time.tm_hour % 10)
475 					result += String (numbers [cur_time.tm_hour % 10]);
476 			}
477 
478 			result += String ((type == 6) ? "点" : "點");
479 
480 			if (cur_time.tm_min < 10)
481 				result += String (numbers [cur_time.tm_min]);
482 			else {
483 				if (cur_time.tm_min >= 20)
484 					result += String (numbers [cur_time.tm_min / 10]);
485 				result += String (numbers [10]);
486 				if (cur_time.tm_min % 10)
487 					result += String (numbers [cur_time.tm_min % 10]);
488 			}
489 
490 			result += String ("分");
491 			break;
492 
493 		default:
494 			snprintf (buf, 80, "%d:%d", cur_time.tm_hour, cur_time.tm_min);
495 			result = String (buf);
496 			break;
497 	}
498 	return utf8_mbstowcs (result);
499 }
500 
501 /*
502 vi:ts=4:nowrap:ai
503 */
504