1 // Copyright (C) 2008-2011 David Sugar, Tycho Softworks.
2 //
3 // This file is part of GNU Bayonne.
4 //
5 // GNU Bayonne is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // GNU Bayonne is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with GNU Bayonne. If not, see <http://www.gnu.org/licenses/>.
17
18 #include <config.h>
19 #include <ucommon/ucommon.h>
20 #include <ucommon/export.h>
22 #include <ctype.h>
23
25 using namespace UCOMMON_NAMESPACE;
26
27 static LinkedObject *primary = NULL;
28 static LinkedObject *secondary = NULL;
29
30 Phrasebook::Phrasebook(bool pri) :
31 LinkedObject()
32 {
33 if(pri)
34 enlist(&primary);
35 else
36 enlist(&secondary);
37 }
38
39 static class __LOCAL _default :
public Phrasebook
40 {
41 public:
42 _default();
43
44 bool id(const char *lang);
45
46 } _default_rule;
47
48 static class __LOCAL _en_US :
public Phrasebook
49 {
50 public:
51 _en_US();
52
53 bool id(const char *lang);
54
55 void time(
const char *text, rule_t *
state);
56
57 void date(
const char *text, rule_t *
state);
58
59 } _en_US_rule;
60
62 {
63 linked_pointer<Phrasebook> rp;
64
65 if(!lang) {
66 #ifdef _MSWINDOWS_
67 #else
68 lang = getenv("LANG");
69 #endif
70 }
71 if(!lang)
72 return &_default_rule;
73
74 rp = secondary;
75 while(is(rp)) {
76 if(rp->id(lang))
77 return *rp;
78 rp.next();
79 }
80
81 rp = primary;
82 while(is(rp)) {
83 if(rp->id(lang))
84 return *rp;
85 rp.next();
86 }
87
88 return NULL;
89 }
90
91 _default::_default() :
93 {
94 }
95
96 _en_US::_en_US() :
98 {
99 }
100
101 bool _en_US::id(const char *lang)
102 {
103 if(case_eq(lang, "en_US"))
104 return true;
105
106 if(case_eq(lang, "en_US.", 6))
107 return true;
108
109 return false;
110 }
111
112 void _en_US::time(
const char *text, rule_t *
state)
113 {
114 const char *ap = "a";
115
116 Time now((char *)text);
117 if(now[Time::hour] >= 12) {
118 ap = "p";
119 unsigned hour = now[Time::hour];
120 if(hour % 12)
121 _lownumber(now[Time::hour] % 12, state);
122 else
123 _lownumber(12, state);
124 }
125 else if(now[Time::hour])
126 _lownumber(now[Time::hour], state);
127 else
128 _lownumber(12, state);
129
130 if(now[Time::minute]) {
131 if(now[Time::minute] < 10)
132 _add("o", state);
133 _lownumber(now[Time::minute], state);
134 }
135
136 _add(ap, state);
137 _add("m", state);
138 }
139
140 void _en_US::date(const char *text, rule_t *state)
141 {
142 static const char *_month[] = {"", "january", "febuary", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"};
143
144 Date now((char *)text);
145 _add(_month[now[Date::month]], state);
146
147 char buf[8];
148 snprintf(buf, sizeof(buf), "%d", now[Date::day]);
149 order(buf, state);
150 }
151
152 bool _default::id(const char *lang)
153 {
154 if(case_eq(lang, "en_", 3))
155 return true;
156
157 if(case_eq(lang, "C"))
158 return true;
159
160 if(case_eq(lang, "en"))
161 return true;
162
163 return false;
164 }
165
166 void Phrasebook::_dup(const char *text, rule_t *state)
167 {
168 size_t len = strlen(text) + 1;
169 if(!state->bp)
170 state->bp = ((char *)(&state->list[state->max])) - 1;
171
172 state->bp -= len;
173 if(state->bp && ((char *)(&state->list[state->pos + 2]) >= state->bp)) {
174 state->list[0] = NULL;
175 state->pos = state->max;
176 }
177 else {
178 String::set(state->bp, len, text);
179 state->list[state->pos++] = state->bp;
180 state->list[state->pos] = NULL;
181 }
182 }
183
184 void Phrasebook::_add(const char *text, rule_t *state)
185 {
186 if(state->bp && ((char *)(&state->list[state->pos + 2]) >= state->bp)) {
187 state->list[0] = NULL;
188 state->pos = state->max;
189 }
190 else if(state->pos >= state->max - 1)
191 state->list[0] = NULL;
192 else {
193 state->list[state->pos++] = text;
194 state->list[state->pos] = NULL;
195 }
196 }
197
198 static const char *_0to19[] =
199 {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
200 "10", "11", "12", "13", "14", "15", "16", "17", "18", "19"};
201
202 static const char *_tens[] =
203 {"", "10", "20", "30", "40", "50", "60", "70", "80", "90"};
204
205 void Phrasebook::_lownumber(int num, rule_t *state)
206 {
207 if(num >= 100)
208 {
209 _add(_0to19[num / 100], state);
210 _add("hundred", state);
211 num %= 100;
212 if(!num)
213 return;
214 }
215 if(num < 20)
216 {
217 _add(_0to19[num], state);
218 return;
219 }
220 _add(_tens[num / 10], state);
221 if(num % 10)
222 _add(_0to19[num % 10], state);
223 }
224
225 void Phrasebook::nonzero(const char *text, rule_t *state)
226 {
227 char *bp = state->bp;
228 unsigned pos = state->pos;
229
230 number(text, state);
231 if(state->zeroflag) {
232 state->bp = bp;
233 state->pos = pos;
234 state->list[pos] = NULL;
235 }
236 }
237
238 void Phrasebook::zero(const char *text, rule_t *state)
239 {
240 if(state->zeroflag)
241 literal(text, state);
242 }
243
244 void Phrasebook::single(const char *text, rule_t *state)
245 {
246 if(state->last == 1)
247 literal(text, state);
248 }
249
250 void Phrasebook::plural(const char *text, rule_t *state)
251 {
252 if(state->last > 1)
253 literal(text, state);
254 }
255
256 void Phrasebook::number(const char *text, rule_t *state)
257 {
258 unsigned long num;
259
260 state->zeroflag = true;
261 state->last = atol(text);
262
263 if(!text || !*text)
264 return;
265
266 if(*text == '-')
267 {
268 _add("negative", state);
269 ++text;
270 }
271 num = atol(text);
272 if(num > 999999999)
273 {
274 state->zeroflag = false;
275 _lownumber(num / 1000000000, state);
276 _add("billion", state);
277 num %= 1000000000;
278 }
279 if(num > 999999)
280 {
281 state->zeroflag = false;
282 _lownumber(num / 1000000, state);
283 _add("million", state);
284 num %= 1000000;
285 }
286 if(num > 999)
287 {
288 state->zeroflag = false;
289 _lownumber(num / 1000, state);
290 _add("thousand", state);
291 num %= 1000;
292 }
293 if(num || state->zeroflag)
294 _lownumber(num, state);
295
296 if(num)
297 state->zeroflag = false;
298
299 text = strchr(text, '.');
300 if(!text)
301 return;
302 _add("point", state);
303 while(*(++text)) {
304 if(*text >= '0' && *text <= '9')
305 _add(_0to19[(*text - '0')], state);
306 }
307 state->zeroflag = false;
308 }
309
310 void Phrasebook::order(const char *text, rule_t *state)
311 {
312 static const char *low[] = { "th",
313 "1st", "2nd", "3rd", "4th", "5th",
314 "6th", "7th", "8th", "9th", "10th",
315 "11th", "12th", "13th", "14th", "15th",
316 "16th", "17th", "18th", "19th"};
317
318 static const char *hi[] = {"", "", "20th", "30th", "40th", "50th", "60th", "70th", "80th", "90th"};
319
320 unsigned num = atoi(text);
321 state->last = atol(text);
322
323 if(num > 100)
324 {
325 if(num % 100) {
326 _add(_0to19[num / 100], state);
327 _add("hundred", state);
328 }
329 else {
330 _add(_0to19[num / 100], state);
331 _add("hundred", state);
332 _add("th", state);
333 }
334 num %= 100;
335 }
336 if(num > 19)
337 {
338 if(num % 10)
339 _add(_tens[num / 10], state);
340 else
341 _add(hi[num / 10], state);
342 num %= 10;
343 }
344 if(num)
345 _add(low[num], state);
346 }
347
348 void Phrasebook::spell(const char *text, rule_t *state)
349 {
350 ucs4_t code;
351 char buf[16];
352
353 if(!state->bp)
354 state->bp = (char *)(&state->list[state->max]);
355
356 while(text && *text) {
357 if(isalnum(*text)) {
358 buf[0] = tolower(*(text++));
359 buf[1] = 0;
360 }
361 else {
362 code = utf8::codepoint(text);
363 if(!code || code == (ucs4_t)EOF)
364 break;
365 text += utf8::size(text);
366 snprintf(buf, sizeof(buf), "cp%lu", (long unsigned)code);
367 }
368 _dup(buf, state);
369 }
370 }
371
372 void Phrasebook::literal(const char *text, rule_t *state)
373 {
374 const char *cp = text;
375 bool dot = false;
376 bool num = true;
377
378 if(!text || !*text)
379 return;
380
381 if(isdigit(text[0]) && isdigit(text[1]) && text[2] == ':') {
382 time(text, state);
383 return;
384 }
385
386 if(isdigit(text[0]) && isdigit(text[1]) && isdigit(text[2]) && isdigit(text[3]) && text[4] == '-') {
387 fulldate(text, state);
388 return;
389 }
390
391
392 if(*cp == '-')
393 ++cp;
394 while(*cp) {
395 if(*cp == '.' && !dot) {
396 dot = true;
397 continue;
398 }
399 if(!isdigit(*cp)) {
400 num = false;
401 break;
402 }
403 ++cp;
404 }
405 if(num)
406 number(text, state);
407 else
408 _add(text, state);
409 }
410
411 void Phrasebook::weekday(const char *text, rule_t *state)
412 {
413 static const char *_dow[] = {"sunday", "monday", "tuesday", "wednasday", "thursday", "friday", "saturday"};
414
415 Date now((char *)text);
416 _add(_dow[now[Date::dow]], state);
417 }
418
419 void Phrasebook::fulldate(const char *text, rule_t *state)
420 {
421 date(text, state);
422 year(text, state);
423 }
424
425 void Phrasebook::date(const char *text, rule_t *state)
426 {
427 static const char *_month[] = {"", "january", "febuary", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december"};
428
429 Date now((char *)text);
430 char buf[8];
431 snprintf(buf, sizeof(buf), "%d", now[Date::day]);
432 order(buf, state);
433 _add(_month[now[Date::month]], state);
434 }
435
436 void Phrasebook::year(const char *text, rule_t *state)
437 {
438 Date now((char *)text);
439 unsigned year = now[Date::year];
440 unsigned hi = year / 100;
441 unsigned lo = year % 100;
442
443 if((hi % 10) == 0) {
444 _lownumber(hi / 10, state);
445 _add("thousand", state);
446 if(!lo)
447 return;
448 _lownumber(lo, state);
449 return;
450 }
451
452 if(!lo) {
453 _lownumber(hi, state);
454 _add("hundred", state);
455 return;
456 }
457
458 _lownumber(hi, state);
459 if(lo < 10)
460 _add("o", state);
461 _lownumber(lo, state);
462 }
463
464 void Phrasebook::time(const char *text, rule_t *state)
465 {
466 Time now((char *)text);
467 _lownumber(now[Time::hour], state);
468 if(now[Time::minute] < 1)
469 _add("o", state);
470 if(now[Time::minute] < 10)
471 _add("o", state);
472 if(now[Time::minute])
473 _lownumber(now[Time::minute], state);
474 }
475
476 void Phrasebook::init(rule_t *state, size_t size)
477 {
478 memset(state, 0, size);
479 state->max = (size - sizeof(rule_t)) / sizeof(char *);
480 state->size = size;
481 }
482
483 void Phrasebook::reset(rule_t *state)
484 {
485 if(state->size)
486 init(state, state->size);
487 }
488
489 const char *Phrasebook::path(void)
490 {
491 return "/en/";
492 }
#define BAYONNE_NAMESPACE
GNU Bayonne library namespace.