Name | Executed | Routines | % | Executed | Lines | % | Unexecuted |
/home/matt/eu/rds/include/std/locale.e | 12 | 13 | 92.31% | 166 | 186 | 89.25% | 20 |
Routine | Executed | Lines | Unexecuted | |
lang_load() | 38 | 45 | 84.44% | 7 |
datetime() | 11 | 14 | 78.57% | 3 |
get_def_lang() | 0 | 2 | 0.00% | 2 |
number() | 34 | 36 | 94.44% | 2 |
get() | 7 | 8 | 87.50% | 1 |
money() | 10 | 11 | 90.91% | 1 |
set() | 11 | 12 | 91.67% | 1 |
set_def_lang() | 5 | 6 | 83.33% | 1 |
translate() | 11 | 12 | 91.67% | 1 |
trsprintf() | 10 | 11 | 90.91% | 1 |
get_lang_path() | 2 | 2 | 100.00% | 0 |
mk_tm_struct() | 12 | 12 | 100.00% | 0 |
set_lang_path() | 3 | 3 | 100.00% | 0 |
# | Executed | |
1 | -- (c) Copyright - See License.txt | |
2 | ||
3 | --**** | |
4 | -- == Locale Routines | |
5 | -- | |
6 | -- < | |
7 | ||
8 | namespace locale | |
9 | ||
10 | include std/dll.e | |
11 | include std/machine.e | |
12 | include std/error.e | |
13 | include std/datetime.e as dt | |
14 | include std/text.e | |
15 | include std/io.e | |
16 | include std/map.e | |
17 | include std/localeconv.e as lcc | |
18 | include std/lcid.e as lcid | |
19 | include std/convert.e | |
20 | include std/filesys.e | |
21 | include std/mathcons.e | |
22 | include std/search.e | |
23 | ||
24 | ||
25 | ------------------------------------------------------------------------------------------ | |
26 | -- | |
27 | -- Local Constants | |
28 | -- | |
29 | ------------------------------------------------------------------------------------------ | |
30 | ||
31 | 1 | constant P = C_POINTER, I = C_INT |
32 | ||
33 | ------------------------------------------------------------------------------------------ | |
34 | -- | |
35 | -- Local Variables | |
36 | -- | |
37 | ------------------------------------------------------------------------------------------ | |
38 | ||
39 | 1 | object def_lang = 0 |
40 | ||
41 | 1 | object lang_path = 0 |
42 | ||
43 | --**** | |
44 | -- === Message translation functions | |
45 | -- | |
46 | ||
47 | --** | |
48 | -- Set the language path. | |
49 | -- | |
50 | -- Parameters: | |
51 | -- # ##pp## : an object, either an actual path or an atom. | |
52 | -- | |
53 | -- Comments: | |
54 | -- When the language path is not set, and it is unset by default, [[:set]]() does not load any language file. | |
55 | -- | |
56 | -- See Also: | |
57 | -- [[:set]] | |
58 | ||
59 | 1 | |
60 | 1 | lang_path = pp |
61 | 1 | end procedure |
62 | ||
63 | --** | |
64 | -- Get the language path. | |
65 | -- | |
66 | -- Returns: | |
67 | -- An **object**, the current language path. | |
68 | -- | |
69 | -- See Also: | |
70 | -- [[:get_lang_path]] | |
71 | ||
72 | 1 | |
73 | 1 | return lang_path |
74 | end function | |
75 | ||
76 | --** | |
77 | -- Load a language file. | |
78 | -- | |
79 | -- Parameters: | |
80 | -- # ##filename## : a sequence, the name of the file to load. If no file | |
81 | -- extension is supplied, then ".lng" is used. | |
82 | -- | |
83 | -- Returns: | |
84 | -- A language **map**, if successful. This is to be used when calling [[:translate]](). | |
85 | -- | |
86 | -- If the load fails it returns a zero. | |
87 | -- | |
88 | -- Comments: | |
89 | -- The language file must be made of lines which are either comments, empty lines | |
90 | -- or translations. Note that leading whitespace is ignored on all lines except | |
91 | -- continuation lines. | |
92 | -- | |
93 | -- * **Comments** are lines that begin with a ~# character and extend to the end of the line. | |
94 | -- * **Empty Lines** are ignored. | |
95 | -- * **Translations** have two forms ... | |
96 | -- | |
97 | -- {{{ | |
98 | -- keyword translation_text | |
99 | -- }}} | |
100 | -- In which the 'keyword' is a word that must not have any spaces in it. | |
101 | -- {{{ | |
102 | -- keyphrase = translation_text | |
103 | -- }}} | |
104 | -- In which the 'keyphrase' is anything up to the first '=' symbol. | |
105 | -- | |
106 | -- It is possible to have the translation text span multiple lines. You do this by | |
107 | -- having '&' as the last character of the line. These are placed by newline characters | |
108 | -- when loading. | |
109 | -- | |
110 | -- Example: | |
111 | --{{{ | |
112 | --# Example translation file | |
113 | --# | |
114 | -- | |
115 | -- | |
116 | --hello Hola | |
117 | --world Mundo | |
118 | --greeting %s, %s! | |
119 | -- | |
120 | --help text = & | |
121 | --This is an example of some & | |
122 | --translation text that spans & | |
123 | --multiple lines. | |
124 | -- | |
125 | -- # End of example PO #2 | |
126 | --}}} | |
127 | -- | |
128 | -- See Also: | |
129 | -- [[:translate]] | |
130 | ||
131 | 2 | |
132 | object lines | |
133 | sequence line, key, msg | |
134 | integer delim | |
135 | integer cont -- continuation | |
136 | map:map keylang | |
137 | map:map altlang | |
138 | ||
139 | 2 | keylang = map:new() |
140 | 2 | altlang = map:new() |
141 | 2 | cont = 0 |
142 | 2 | filename = defaultext(filename, "lng") |
143 | ||
144 | 2 | if sequence(lang_path) and length(lang_path) > 0 then |
145 | 0 | sequence tempname |
146 | 0 | tempname = locate_file(filename, {lang_path}) |
147 | 0 | if equal(tempname, filename) then |
148 | 0 | filename = locate_file(filename) |
149 | else | |
150 | 0 | filename = tempname |
151 | end if | |
152 | else | |
153 | 2 | filename = locate_file(filename) |
154 | end if | |
155 | ||
156 | 2 | lines = read_lines(filename) |
157 | 2 | if atom(lines) then |
158 | 0 | return 0 -- Language maps unchanged. |
159 | end if | |
160 | ||
161 | 2 | for i = 1 to length(lines) do |
162 | ||
163 | 25 | if cont then |
164 | ||
165 | 4 | line = trim_tail(lines[i]) |
166 | 4 | if line[$] = '&' then |
167 | 2 | msg &= line[1..$-1] & '\n' |
168 | else | |
169 | 2 | msg &= line |
170 | 2 | map:put(keylang, key, msg) |
171 | 2 | map:put(altlang, msg, key) |
172 | 2 | cont = 0 |
173 | end if | |
174 | else | |
175 | 21 | line = trim(lines[i]) |
176 | 21 | if length(line) = 0 then |
177 | 7 | continue |
178 | end if | |
179 | 14 | if line[1] = '#' then |
180 | 6 | continue |
181 | end if | |
182 | ||
183 | 8 | delim = find('=', line) |
184 | 8 | if delim = 0 then |
185 | 7 | delim = find(' ', line) |
186 | end if | |
187 | ||
188 | 8 | if delim = 0 then |
189 | -- Ignore lines with missing translation text | |
190 | 0 | continue |
191 | end if | |
192 | ||
193 | 8 | key = trim(line[1..delim-1]) |
194 | 8 | if line[$] = '&' then |
195 | 2 | cont = 1 |
196 | 2 | msg = trim(line[delim+1..$-1]) |
197 | 2 | if length(msg) > 0 then |
198 | 1 | msg &= '\n' |
199 | end if | |
200 | else | |
201 | 6 | msg = trim(line[delim+1..$]) |
202 | 6 | map:put(keylang, key, msg) |
203 | 6 | map:put(altlang, msg, key) |
204 | end if | |
205 | end if | |
206 | 12 | end for |
207 | ||
208 | 2 | return {keylang, altlang} |
209 | end function | |
210 | ||
211 | --** | |
212 | -- Sets the default language (translation) map | |
213 | -- | |
214 | -- Parameters: | |
215 | -- # ##langmap## : A value returned by [[:lang_load]](), or zero to remove any default map. | |
216 | -- | |
217 | -- Example: | |
218 | -- | |
219 | -- set_def_lang( lang_load("appmsgs") ) | |
220 | -- | |
221 | ||
222 | 2 | |
223 | 2 | if atom(langmap) and langmap = 0 then |
224 | 0 | def_lang = langmap |
225 | 2 | elsif length(langmap) = 2 then |
226 | 2 | def_lang = langmap |
227 | end if | |
228 | 2 | end procedure |
229 | ||
230 | --** | |
231 | -- Gets the default language (translation) map | |
232 | -- | |
233 | -- Parameters: | |
234 | -- none. | |
235 | -- | |
236 | -- Returns: | |
237 | -- An **object**, a language map, or zero if there is no default language map yet. | |
238 | -- | |
239 | -- Example: | |
240 | -- | |
241 | -- object langmap = get_def_lang() | |
242 | -- | |
243 | ||
244 | 0 | |
245 | 0 | return def_lang |
246 | end function | |
247 | ||
248 | --** | |
249 | -- Translates a word, using the current language file. | |
250 | -- | |
251 | -- Parameters: | |
252 | -- # ##word## : a sequence, the word to translate. | |
253 | -- # ##langmap## : Either a value returned by [[:lang_load]]() or zero to use the default language map | |
254 | -- # ##defval## : a object. The value to return if the word cannot be translated. | |
255 | -- Default is "". If ##defval## is ##PINF## then the ##word## is returned | |
256 | -- if it can't be translated. | |
257 | -- # ##mode## : an integer. If zero (the default) it uses ##word## as the keyword and returns | |
258 | -- the translation text. If not zero it uses ##word## | |
259 | -- as the translation and returns the keyword. | |
260 | -- | |
261 | -- Returns: | |
262 | -- A **sequence**, the value associated with ##word##, or ##defval## if there | |
263 | -- is no association. | |
264 | -- | |
265 | -- Example 1: | |
266 | -- | |
267 | -- sequence newword | |
268 | -- newword = translate(msgtext) | |
269 | -- if length(msgtext) = 0 then | |
270 | -- error_message(msgtext) | |
271 | -- else | |
272 | -- error_message(newword) | |
273 | -- end if | |
274 | -- | |
275 | -- | |
276 | -- Example 2: | |
277 | -- | |
278 | -- error_message(translate(msgtext, , PINF)) | |
279 | -- | |
280 | -- | |
281 | -- See Also: | |
282 | -- [[:set]], [[:lang_load]] | |
283 | ||
284 | 25 | |
285 | 25 | if equal(defval, PINF) then |
286 | 7 | defval = word |
287 | end if | |
288 | ||
289 | 25 | if equal(langmap, 0) then |
290 | 24 | if equal(def_lang, 0) then |
291 | -- No default language map loaded yet. | |
292 | 1 | return defval |
293 | else | |
294 | 23 | langmap = def_lang |
295 | end if | |
296 | end if | |
297 | 24 | if atom(langmap) or length(langmap) != 2 then |
298 | -- Not a valid language map passed. | |
299 | 0 | return defval |
300 | end if | |
301 | ||
302 | 24 | if mode = 0 then |
303 | 23 | return map:get(langmap[1], word, defval) |
304 | else | |
305 | 1 | return map:get(langmap[2], word, defval) |
306 | end if | |
307 | ||
308 | end function | |
309 | ||
310 | --** | |
311 | -- Returns a formatted string with automatic translation performed on the parameters. | |
312 | -- | |
313 | -- Parameters: | |
314 | -- # ##fmt## : A sequence. Contains the formatting string. see [[:printf]]() for details. | |
315 | -- # ##data## : A sequence. Contains the data that goes into the formatted result. see [[:printf]] for details. | |
316 | -- # ##langmap## : An object. Either 0 (the default) to use the default language maps, or | |
317 | -- the result returned from [[:lang_load]]() to specify a particular | |
318 | -- language map. | |
319 | -- | |
320 | -- Returns: | |
321 | -- A **sequence**, the formatted result. | |
322 | -- | |
323 | -- Comments: | |
324 | -- This works very much like the [[:sprintf]]() function. The difference is that the ##fmt## sequence | |
325 | -- and sequences contained in the ##data## parameter are [[:translate | translated ]] before | |
326 | -- passing them to [[:sprintf]]. If an item has no translation, it remains unchanged. | |
327 | -- | |
328 | -- Further more, after the translation pass, if the result text begins with {{{"__"}}}, | |
329 | -- the {{{"__"}}} is removed. | |
330 | -- This method can be used when you do not want an item to be translated. | |
331 | -- | |
332 | -- Examples: | |
333 | -- | |
334 | -- -- Assuming a language has been loaded and | |
335 | -- -- "greeting" translates as '%s %s, %s' | |
336 | -- -- "hello" translates as "G'day" | |
337 | -- -- "how are you today" translates as "How's the family?" | |
338 | -- sequence UserName = "Bob" | |
339 | -- sequence result = trsprintf( "greeting", {"hello", "__" & UserName, "how are you today"}) | |
340 | -- --> "G'day Bob, How's the family?" | |
341 | -- | |
342 | -- | |
343 | 2 | |
344 | 2 | for i = 1 to length(data) do |
345 | 4 | if sequence(data[i]) then |
346 | 4 | data[i] = translate(data[i], langmap, PINF) |
347 | 4 | if begins("__", data[i]) then |
348 | 1 | data[i] = data[i][3 .. $] |
349 | end if | |
350 | end if | |
351 | 4 | end for |
352 | 2 | fmt = translate(fmt, langmap, PINF) |
353 | 2 | if begins("__", fmt) then |
354 | 0 | fmt = fmt[3 .. $] |
355 | end if | |
356 | ||
357 | 2 | return sprintf(fmt, data) |
358 | end function | |
359 | ||
360 | ||
361 | ------------------------------------------------------------------------------------------ | |
362 | -- | |
363 | -- Library Open/Checking | |
364 | -- | |
365 | ------------------------------------------------------------------------------------------ | |
366 | ||
367 | 1 | ifdef WIN32 then |
368 | constant | |
369 | lib = open_dll("MSVCRT.DLL"), | |
370 | lib2 = open_dll("KERNEL32.DLL"), | |
371 | f_strfmon = define_c_func(lib2, "GetCurrencyFormatA", {I, I, P, P, P, I}, I), | |
372 | f_strfnum = define_c_func(lib2, "GetNumberFormatA", {I, I, P, P, P, I}, I), | |
373 | f_setlocale = define_c_func(lib, "setlocale", {I, P}, P), | |
374 | f_strftime = define_c_func(lib, "strftime", {P, I, P, P}, I), | |
375 | LC_ALL = 0, | |
376 | -- LC_COLLATE = 1, | |
377 | -- LC_CTYPE = 2, | |
378 | LC_MONETARY = 3, | |
379 | LC_NUMERIC = 4, | |
380 | -- LC_TIME = 5, | |
381 | -- LC_MESSAGES = 6, | |
382 | $ | |
383 | ||
384 | /* constant | |
385 | FORMAT_SIZE = 6 * 4, | |
386 | NUM_DIGITS = 0, | |
387 | LEADING_ZERO = 4, | |
388 | GROUPING = 8, | |
389 | DECIMAL_SEP = 12, | |
390 | THOUSANDS_SEP = 16, | |
391 | NEGATIVE_ORDER = 20 | |
392 | */ | |
393 | sequence current_locale = "" | |
394 | ||
395 | elsifdef LINUX then | |
396 | constant | |
397 | 1 | lib = open_dll(""), |
398 | 1 | f_strfmon = define_c_func(lib, "strfmon", {P, I, P, C_DOUBLE}, I), |
399 | 1 | f_strfnum = -1, |
400 | 1 | f_setlocale = define_c_func(lib, "setlocale", {I, P}, P), |
401 | 1 | f_strftime = define_c_func(lib, "strftime", {P, I, P, P}, I), |
402 | 1 | LC_ALL = 6, |
403 | -- LC_CTYPE = 0, | |
404 | 1 | LC_NUMERIC = 1, |
405 | -- LC_TIME = 2, | |
406 | -- LC_COLLATE = 3, | |
407 | 1 | LC_MONETARY = 4, |
408 | -- LC_MESSAGES = 5, | |
409 | $ | |
410 | ||
411 | elsifdef FREEBSD or SUNOS then | |
412 | constant | |
413 | lib = open_dll("libc.so"), | |
414 | f_strfmon = define_c_func(lib, "strfmon", {P, I, P, C_DOUBLE}, I), | |
415 | f_strfnum = -1, | |
416 | f_setlocale = define_c_func(lib, "setlocale", {I, P}, P), | |
417 | f_strftime = define_c_func(lib, "strftime", {P, I, P, P}, I), | |
418 | LC_ALL = 0, | |
419 | -- LC_COLLATE = 1, | |
420 | -- LC_CTYPE = 2, | |
421 | LC_MONETARY = 3, | |
422 | LC_NUMERIC = 4, | |
423 | -- LC_TIME = 5, | |
424 | -- LC_MESSAGES = 6, | |
425 | $ | |
426 | ||
427 | elsifdef OSX then | |
428 | constant | |
429 | lib = open_dll("libc.dylib"), | |
430 | f_strfmon = define_c_func(lib, "strfmon", {P, I, P, C_DOUBLE}, I), | |
431 | f_strfnum = -1, | |
432 | f_setlocale = define_c_func(lib, "setlocale", {I, P}, P), | |
433 | f_strftime = define_c_func(lib, "strftime", {P, I, P, P}, I), | |
434 | LC_ALL = 0, | |
435 | -- LC_COLLATE = 1, | |
436 | -- LC_CTYPE = 2, | |
437 | LC_MONETARY = 3, | |
438 | LC_NUMERIC = 4, | |
439 | -- LC_TIME = 5, | |
440 | -- LC_MESSAGES = 6, | |
441 | $ | |
442 | ||
443 | elsedef | |
444 | ||
445 | constant | |
446 | lib = -1 | |
447 | lib2 = -1 | |
448 | f_strfmon = -1 | |
449 | f_strfnum = -1 | |
450 | f_setlocale = -1 | |
451 | f_strftime = -1 | |
452 | LC_ALL = -1, | |
453 | -- LC_COLLATE = -1, | |
454 | -- LC_CTYPE = -1, | |
455 | LC_MONETARY = -1, | |
456 | LC_NUMERIC = -1, | |
457 | -- LC_TIME = -1, | |
458 | -- LC_MESSAGES = -1, | |
459 | $ | |
460 | ||
461 | end ifdef | |
462 | ||
463 | --**** | |
464 | -- === Time/Number Translation | |
465 | ||
466 | --** | |
467 | -- Set the computer locale, and possibly load appropriate translation file. | |
468 | -- | |
469 | -- Parameters: | |
470 | -- # ##new_locale## : a sequence representing a new locale. | |
471 | -- | |
472 | -- Returns: | |
473 | -- An **integer**, either 0 on failure or 1 on success. | |
474 | -- | |
475 | -- Comments: | |
476 | -- Locale strings have the following format: xx_YY or xx_YY.xyz . | |
477 | -- The xx part refers to a culture, or main language/script. For instance, "en" refers to | |
478 | -- English, "de" refers to German, and so on. For some language, a script may be specified, | |
479 | -- like in "mn_Cyrl_MN" (mongolian in cyrillic transcription). | |
480 | -- | |
481 | -- The YY part refers to a subculture, or variant, of the main language. For instance, "fr_FR" | |
482 | -- refers to metropolitan France, while "fr_BE" refers to the variant spoken in Wallonie, the | |
483 | -- French speaking region of Belgium. | |
484 | -- | |
485 | -- The optional .xyz part specifies an encoding, like .utf8 or .1252 . This is required in some cases. | |
486 | ||
487 | 3 | |
488 | atom lAddr_localename | |
489 | atom ign | |
490 | sequence nlocale | |
491 | ||
492 | 3 | nlocale = lcc:decanonical(new_locale) |
493 | 3 | lAddr_localename = allocate_string(nlocale) |
494 | 3 | ign = c_func(f_setlocale, {LC_MONETARY, lAddr_localename}) |
495 | 3 | ign = c_func(f_setlocale, {LC_NUMERIC, lAddr_localename}) |
496 | 3 | ign = c_func(f_setlocale, {LC_ALL, lAddr_localename}) |
497 | 3 | free(lAddr_localename) |
498 | ||
499 | 3 | if sequence(lang_path) then |
500 | -- Note: Failure to update language map does not fail this function. | |
501 | 0 | def_lang = lang_load(nlocale) |
502 | end if | |
503 | ||
504 | 3 | ign = (ign != NULL) |
505 | 3 | ifdef WIN32 then |
506 | if ign then | |
507 | current_locale = new_locale | |
508 | end if | |
509 | end ifdef | |
510 | 3 | return ign |
511 | end function | |
512 | ||
513 | --** | |
514 | -- Get current locale string | |
515 | -- | |
516 | -- Returns: | |
517 | -- A **sequence**, a locale string. | |
518 | -- | |
519 | -- See Also: | |
520 | -- [[:set]] | |
521 | ||
522 | 3 | |
523 | sequence r | |
524 | atom p | |
525 | ||
526 | 3 | p = c_func(f_setlocale, {LC_ALL, NULL}) |
527 | 3 | if p = NULL then |
528 | 0 | return "" |
529 | end if | |
530 | ||
531 | 3 | r = peek_string(p) |
532 | 3 | ifdef WIN32 then |
533 | if equal(lcc:decanonical(r), lcc:decanonical(current_locale)) then | |
534 | return current_locale | |
535 | end if | |
536 | end ifdef | |
537 | 3 | r = lcc:canonical(r) |
538 | ||
539 | 3 | return r |
540 | end function | |
541 | ||
542 | --** | |
543 | -- Converts an amount of currency into a string representing that amount. | |
544 | -- | |
545 | -- Parameters: | |
546 | -- # ##amount## : an atom, the value to write out. | |
547 | -- | |
548 | -- Returns: | |
549 | -- A **sequence**, a string that writes out ##amount## of current currency. | |
550 | -- | |
551 | -- Example 1: | |
552 | -- | |
553 | -- -- Assuming an en_US locale | |
554 | -- ? money(1020.5) -- returns"$1,020.50" | |
555 | -- | |
556 | -- | |
557 | -- See Also: | |
558 | -- [[:set]], [[:number]] | |
559 | ||
560 | 1 | |
561 | sequence result | |
562 | integer size | |
563 | atom pResult, pTmp | |
564 | ||
565 | 1 | if f_strfmon != -1 then |
566 | 1 | ifdef UNIX then |
567 | 1 | pResult = allocate(4 * 160) |
568 | 1 | pTmp = allocate_string("%n") |
569 | 1 | size = c_func(f_strfmon, {pResult, 4 * 160, pTmp, amount}) |
570 | elsifdef WIN32 then | |
571 | pResult = allocate(4 * 160) | |
572 | pTmp = allocate_string(sprintf("%.8f", {amount})) | |
573 | size = c_func(f_strfmon, {lcid:get_lcid(get()), 0, pTmp, NULL, pResult, 4 * 160}) | |
574 | end ifdef | |
575 | ||
576 | 1 | result = peek_string(pResult) |
577 | 1 | free(pResult) |
578 | 1 | free(pTmp) |
579 | ||
580 | 1 | return result |
581 | else | |
582 | 0 | return text:format("$[,,.2]", amount) |
583 | end if | |
584 | end function | |
585 | ||
586 | --** | |
587 | -- Converts a number into a string representing that number. | |
588 | -- | |
589 | -- Parameters: | |
590 | -- # ##num## : an atom, the value to write out. | |
591 | -- | |
592 | -- Returns: | |
593 | -- A **sequence**, a string that writes out ##num##. | |
594 | -- | |
595 | -- Example 1: | |
596 | -- | |
597 | -- -- Assuming an en_US locale | |
598 | -- ? number(1020.5) -- returns "1,020.50" | |
599 | -- | |
600 | -- | |
601 | -- See Also: | |
602 | -- [[:set]], [[:money]] | |
603 | ||
604 | 4 | |
605 | sequence result | |
606 | integer size | |
607 | atom pResult, pTmp | |
608 | ||
609 | 4 | ifdef UNIX then |
610 | 4 | if f_strfmon != -1 then |
611 | 4 | pResult = allocate(4 * 160) |
612 | 4 | if integer(num) then |
613 | 1 | pTmp = allocate_string("%!.0n") |
614 | else | |
615 | 3 | pTmp = allocate_string("%!n") |
616 | end if | |
617 | 4 | size = c_func(f_strfmon, {pResult, 4 * 160, pTmp, num}) |
618 | else | |
619 | 0 | return text:format("[,,]", num) |
620 | end if | |
621 | ||
622 | elsifdef WIN32 then | |
623 | atom lpFormat | |
624 | if f_strfnum != -1 then | |
625 | pResult = allocate(4 * 160) | |
626 | if integer(num) then | |
627 | pTmp = allocate_string(sprintf("%d", {num})) | |
628 | -- lpFormat must not only be allocated but completely populated | |
629 | -- with values from the current locale. | |
630 | lpFormat = NULL | |
631 | else | |
632 | lpFormat = NULL | |
633 | pTmp = allocate_string(sprintf("%.15f", {num})) | |
634 | end if | |
635 | size = c_func(f_strfnum, {lcid:get_lcid(get()), 0, pTmp, lpFormat, pResult, 4 * 160}) | |
636 | else | |
637 | return text:format("[,,]", num) | |
638 | end if | |
639 | ||
640 | elsedef | |
641 | return text:format("[,,]", num) | |
642 | end ifdef | |
643 | ||
644 | 4 | result = peek_string(pResult) |
645 | 4 | free(pResult) |
646 | 4 | free(pTmp) |
647 | ||
648 | -- Assumption: All locales remove trailing zeros and decimal point from integers | |
649 | 4 | integer is_int = integer(num) |
650 | 4 | if is_int = 0 then |
651 | 3 | sequence float = sprintf("%.15f", num) |
652 | 3 | is_int = find('.', float) |
653 | 3 | if is_int then |
654 | 3 | for i = length(float) to is_int+1 by -1 do |
655 | 31 | if float[i] != '0' then |
656 | 2 | is_int = 0 |
657 | 2 | exit |
658 | end if | |
659 | 29 | end for |
660 | end if | |
661 | end if | |
662 | 4 | if is_int != 0 then -- The input was an integer |
663 | -- remove all trailing zeros and decimal point, but not any trailing symbols -- | |
664 | -- Step 1. Locate rightmost digit. | |
665 | 2 | is_int = 0 |
666 | 2 | for i = length(result) to 1 by -1 do |
667 | 2 | if find(result[i], "1234567890") then |
668 | 2 | is_int = i + 1 |
669 | 2 | exit |
670 | end if | |
671 | 0 | end for |
672 | -- Step 2. From rightmost digit, stop when we get to a non-zero | |
673 | 2 | for i = is_int - 1 to 1 by -1 do |
674 | 5 | if result[i] != '0' then |
675 | 2 | if not find(result[i], "1234567890") then |
676 | -- Step 3. Remove decimal point and all zeros, preserving any trailing symbols. | |
677 | 1 | result = result[1..i-1] & result[is_int .. $] |
678 | end if | |
679 | 2 | exit |
680 | end if | |
681 | 3 | end for |
682 | end if | |
683 | ||
684 | 4 | return result |
685 | end function | |
686 | ||
687 | 1 | |
688 | atom pDtm | |
689 | ||
690 | 1 | pDtm = allocate(36) |
691 | 1 | poke4(pDtm, dtm[SECOND]) -- int tm_sec |
692 | 1 | poke4(pDtm+4, dtm[MINUTE]) -- int tm_min |
693 | 1 | poke4(pDtm+8, dtm[HOUR]) -- int tm_hour |
694 | 1 | poke4(pDtm+12, dtm[DAY]) -- int tm_mday |
695 | 1 | poke4(pDtm+16, dtm[MONTH] - 1) -- int tm_mon |
696 | 1 | poke4(pDtm+20, dtm[YEAR] - 1900) -- int tm_year |
697 | 1 | poke4(pDtm+24, dt:weeks_day(dtm) - 1) -- int tm_wday |
698 | 1 | poke4(pDtm+28, dt:years_day(dtm)) -- int tm_yday |
699 | 1 | poke4(pDtm+32, 0) -- int tm_isdst |
700 | ||
701 | 1 | return pDtm |
702 | end function | |
703 | ||
704 | --** | |
705 | -- Formats a date according to current locale. | |
706 | -- | |
707 | -- Parameters: | |
708 | -- # ##fmt## : A format string, as described in datetime:[[:format]] | |
709 | -- # ##dtm## : the datetime to write out. | |
710 | -- | |
711 | -- Returns: | |
712 | -- A **sequence**, representing the formatted date. | |
713 | -- | |
714 | -- Example 1: | |
715 | -- | |
716 | -- ? datetime("Today is a %A",dt:now()) | |
717 | -- | |
718 | -- | |
719 | -- See Also: | |
720 | -- datetime:[[:format]] | |
721 | ||
722 | 1 | |
723 | atom pFmt, pRes, pDtm | |
724 | integer size | |
725 | sequence res | |
726 | ||
727 | 1 | if f_strftime != -1 then |
728 | 1 | pDtm = mk_tm_struct(dtm) |
729 | 1 | pFmt = allocate_string(fmt) |
730 | 1 | pRes = allocate(1024) |
731 | 1 | size = c_func(f_strftime, {pRes, 256, pFmt, pDtm}) |
732 | 1 | res = peek_string(pRes) |
733 | 1 | free(pRes) |
734 | 1 | free(pFmt) |
735 | 1 | free(pDtm) |
736 | else | |
737 | 0 | res = date() |
738 | 0 | res[1] += 1900 |
739 | 0 | res = dt:format(res[1..6], fmt) |
740 | end if | |
741 | ||
742 | 1 | return res |
743 | end function |