source: branches/branch-1-0/src/helpers/nls.c@ 406

Last change on this file since 406 was 386, checked in by pr, 15 years ago

Fix rounding error.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 31.0 KB
Line 
1
2/*
3 *@@sourcefile nls.c:
4 * contains a few helpers for National Language Support (NLS),
5 * such as printing strings with the format specified by
6 * the "Country" object.
7 *
8 * Usage: All OS/2 programs.
9 *
10 * Function prefixes (new with V0.81):
11 * -- nls* NLS helpers
12 *
13 * This file is new with 0.9.16, but contains functions
14 * formerly in stringh.c.
15 *
16 * Note: Version numbering in this file relates to XWorkplace version
17 * numbering.
18 *
19 *@@header "helpers\nls.h"
20 *@@added V0.9.16 (2001-10-11) [umoeller]
21 */
22
23/*
24 * Copyright (C) 1997-2010 Ulrich M”ller.
25 * This file is part of the "XWorkplace helpers" source package.
26 * This is free software; you can redistribute it and/or modify
27 * it under the terms of the GNU General Public License as published
28 * by the Free Software Foundation, in version 2 as it comes in the
29 * "COPYING" file of the XWorkplace main distribution.
30 * This program is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 * GNU General Public License for more details.
34 */
35
36#define OS2EMX_PLAIN_CHAR
37 // this is needed for "os2emx.h"; if this is defined,
38 // emx will define PSZ as _signed_ char, otherwise
39 // as unsigned char
40
41#define INCL_DOSNLS
42#define INCL_DOSDATETIME
43#define INCL_DOSERRORS
44#define INCL_WINSHELLDATA
45#include <os2.h>
46
47#include <stdlib.h>
48#include <stdio.h>
49#include <string.h>
50#include <math.h>
51#include <unidef.h>
52#include <uconv.h>
53
54// XWP's setup.h replaces strchr and the like, and
55// we want the originals in here
56#define DONT_REPLACE_FOR_DBCS
57#include "setup.h" // code generation and debugging options
58
59#include "helpers\dosh.h"
60#include "helpers\nls.h"
61#include "helpers\prfh.h"
62#include "helpers\standards.h"
63
64#pragma hdrstop
65
66// WarpIN V1.0.15 (2007-03-26) [pr]: @@fixes 936
67// UCONV functions
68int (* CALLCONV G_UniCreateUconvObject)(UniChar *code_set, UconvObject *uobj) = NULL;
69int (* CALLCONV G_UniStrFromUcs)(UconvObject co, char *target, UniChar *source,
70 int len) = NULL;
71int (* CALLCONV G_UniFreeUconvObject)(UconvObject uobj) = NULL;
72// LIBUNI functions
73int (* CALLCONV G_UniCreateLocaleObject)(int locale_spec_type,
74 const void *locale_spec, LocaleObject *locale_object_ptr) = NULL;
75int (* CALLCONV G_UniQueryLocaleItem)(const LocaleObject locale_object,
76 LocaleItem item, UniChar **info_item_addr_ptr) = NULL;
77size_t (* CALLCONV G_UniStrlen)(const UniChar *ucs1) = NULL;
78int (* CALLCONV G_UniFreeMem)(void *memory_ptr) = NULL;
79int (* CALLCONV G_UniFreeLocaleObject)(LocaleObject locale_object) = NULL;
80
81/*
82 * G_aResolveFromUCONV:
83 * functions resolved from UCONV.DLL.
84 * Used with doshResolveImports.
85 */
86
87static const RESOLVEFUNCTION G_aResolveFromUCONV[] =
88{
89 "UniCreateUconvObject", (PFN*)&G_UniCreateUconvObject,
90 "UniStrFromUcs", (PFN*)&G_UniStrFromUcs,
91 "UniFreeUconvObject", (PFN*)&G_UniFreeUconvObject
92};
93
94/*
95 * G_aResolveFromLIBUNI:
96 * functions resolved from LIBUNI.DLL.
97 * Used with doshResolveImports.
98 */
99
100static const RESOLVEFUNCTION G_aResolveFromLIBUNI[] =
101{
102 "UniCreateLocaleObject", (PFN*)&G_UniCreateLocaleObject,
103 "UniQueryLocaleItem", (PFN*)&G_UniQueryLocaleItem,
104 "UniStrlen", (PFN*)&G_UniStrlen,
105 "UniFreeMem", (PFN*)&G_UniFreeMem,
106 "UniFreeLocaleObject", (PFN*)&G_UniFreeLocaleObject
107};
108
109static BOOL fUniResolved = FALSE, fUniOK = FALSE;
110
111/*
112 *@@category: Helpers\National Language Support
113 * See nls.c.
114 */
115
116/* ******************************************************************
117 *
118 * DBCS support
119 *
120 ********************************************************************/
121
122#define MAX_LEADBYTE 256
123
124#pragma pack(1)
125
126typedef struct _DBCSVECTOR
127{
128 BYTE bLow;
129 BYTE bHigh;
130} DBCSVECTOR;
131
132#pragma pack()
133
134BOOL G_afLeadByte[MAX_LEADBYTE] = {0};
135ULONG G_fDBCS = 2; // not queried yet
136COUNTRYCODE G_cc = { 0, 0 };
137DBCSVECTOR G_aDBCSVector[8];
138
139/*
140 *@@ nlsDBCS:
141 * returns TRUE if the system is currently using DBCS.
142 *
143 *@@added V0.9.19 (2002-06-13) [umoeller]
144 *@@changed V0.9.20 (2002-07-03) [umoeller]: fixed, this never worked
145 */
146
147BOOL nlsDBCS(VOID)
148{
149 APIRET arc;
150
151 if (G_fDBCS != 2)
152 // already queried:
153 return G_fDBCS;
154
155 // V0.9.20 (2002-07-03) [umoeller]
156 // assume a non-DBCS system UNLESS the below
157 // loop gives us something meaningful; even
158 // on non-DBCS systems like mine, DosQueryDBCSEnv
159 // does not return an error
160 G_fDBCS = FALSE;
161
162 if (!(arc = DosQueryDBCSEnv(8 * sizeof(DBCSVECTOR),
163 &G_cc,
164 (PCHAR)G_aDBCSVector)))
165 {
166 int i;
167 for (i = 0;
168 i < 8;
169 ++i)
170 {
171 if ( (G_aDBCSVector[i].bLow)
172 && (G_aDBCSVector[i].bHigh)
173 )
174 {
175 int n;
176 for (n = G_aDBCSVector[i].bLow;
177 n <= G_aDBCSVector[i].bHigh;
178 ++n)
179 G_afLeadByte[n] = TRUE;
180
181 G_fDBCS = TRUE;
182 }
183 else
184 break;
185 }
186 }
187
188 return G_fDBCS;
189}
190
191/*
192 *@@ nlsQueryDBCSChar:
193 * returns the type of the DBCS character with
194 * the given index. Note that the index is the
195 * DBCS character index, not just the array
196 * index into the CHAR array.
197 *
198 * Returns:
199 *
200 * -- TYPE_SBCS: ulOfs is single byte.
201 *
202 * -- TYPE_DBCS_1ST: ulOfs is a double-byte lead char.
203 *
204 * -- TYPE_DBCS_2ND: ulOfs is a double-byte trail char.
205 *
206 * Preconditions:
207 *
208 * -- nlsDBCS must have been called to initialize our
209 * globals, and must have returned TRUE.
210 *
211 *@@added V0.9.19 (2002-06-13) [umoeller]
212 */
213
214ULONG nlsQueryDBCSChar(PCSZ pcszString,
215 ULONG ulOfs)
216
217{
218 ULONG ulDBCSType = TYPE_SBCS;
219 ULONG i;
220
221 for (i = 0;
222 i <= ulOfs;
223 ++i)
224 {
225 switch (ulDBCSType)
226 {
227 case TYPE_SBCS:
228 case TYPE_DBCS_2ND:
229 ulDBCSType = G_afLeadByte[pcszString[i]];
230 break;
231
232 case TYPE_DBCS_1ST :
233 ulDBCSType = TYPE_DBCS_2ND;
234 break;
235 }
236 }
237
238 return ulDBCSType;
239}
240
241/*
242 *@@ nlschr:
243 * replacement for strchr with DBCS support.
244 *
245 * If the system is not running with DBCS,
246 * this calls plain strchr automatically.
247 *
248 *@@added V0.9.19 (2002-06-13) [umoeller]
249 *@@changed V0.9.20 (2002-07-22) [umoeller]: optimized
250 *@@changed V0.9.20 (2002-07-22) [lafaix]: optimized
251 */
252
253PSZ nlschr(PCSZ p, char c)
254{
255 PCSZ p2;
256 ULONG ulDBCSType = TYPE_SBCS;
257
258 if (!nlsDBCS())
259 // not DBCS:
260 return strchr(p, c);
261
262 // we're on DBCS:
263
264 // we can't find c if it is a leading byte
265 if (G_afLeadByte[c] != TYPE_SBCS)
266 return NULL;
267
268 for (p2 = p;
269 *p2;
270 ++p2)
271 {
272 // check _previous_ DBCS type and refresh
273 // DBCS type accordingly
274 switch (ulDBCSType)
275 {
276 case TYPE_SBCS:
277 case TYPE_DBCS_2ND:
278 ulDBCSType = G_afLeadByte[*p2];
279 // we can safely do the test here (and skip rechecking
280 // the type) because c can't possibly be a leading byte
281 // V0.9.20 (2002-07-22) [lafaix]
282 if (*p2 == c)
283 return (PSZ)p2;
284 break;
285
286 case TYPE_DBCS_1ST :
287 ulDBCSType = TYPE_DBCS_2ND;
288 break;
289 }
290 }
291
292 /* old code V0.9.20 (2002-07-22) [umoeller]
293 // we're on DBCS:
294 for (p2 = p;
295 *p2;
296 ++p2)
297 {
298 if (*p2 == c)
299 {
300 // match: return this only if it's SBCS;
301 // if it's a DBCS lead char, skip it
302 switch (ulDBCS = nlsQueryDBCSChar(p, p2 - p))
303 {
304 case TYPE_SBCS:
305 return (PSZ)p2;
306
307 case TYPE_DBCS_1ST:
308 ++p2;
309 }
310 }
311 }
312 */
313
314 return NULL;
315}
316
317/*
318 *@@ nlsrchr:
319 * replacement for strrchr with DBCS support.
320 *
321 * If the system is not running with DBCS,
322 * this calls plain strrchr automatically.
323 *
324 *@@added V0.9.19 (2002-06-13) [umoeller]
325 *@@changed V0.9.20 (2002-07-22) [lafaix]: optimized
326 */
327
328PSZ nlsrchr(PCSZ p, char c)
329{
330 PCSZ p2,
331 pLast = NULL;
332 ULONG ulDBCSType = TYPE_SBCS;
333
334 if (!nlsDBCS())
335 // not DBCS:
336 return strrchr(p, c);
337
338 // we're on DBCS:
339
340 // we can't find c if it is a leading byte
341 if (G_afLeadByte[c] != TYPE_SBCS)
342 return NULL;
343
344 for (p2 = p;
345 *p2;
346 ++p2)
347 {
348 // check _previous_ DBCS type and refresh
349 // DBCS type accordingly
350 switch (ulDBCSType)
351 {
352 case TYPE_SBCS:
353 case TYPE_DBCS_2ND:
354 ulDBCSType = G_afLeadByte[*p2];
355 if (*p2 == c)
356 pLast = p2;
357 break;
358
359 case TYPE_DBCS_1ST :
360 ulDBCSType = TYPE_DBCS_2ND;
361 break;
362 }
363 }
364
365 return (PSZ)pLast;
366
367 // old code V0.9.20 (2002-07-22) [lafaix]
368 /*
369 ulLength = strlen(p);
370 for (p2 = p + ulLength - 1;
371 p2 >= p;
372 --p2)
373 {
374 if (*p2 == c)
375 {
376 // match: return this only if it's SBCS;
377 // if it's a DBCS trail char, skip it
378 switch (ulDBCS = nlsQueryDBCSChar(p, p2 - p))
379 {
380 case TYPE_SBCS:
381 return (PSZ)p2;
382
383 case TYPE_DBCS_2ND:
384 --p2;
385 }
386 }
387 }
388
389 return NULL;
390 */
391}
392
393/* ******************************************************************
394 *
395 * Country-dependent formatting
396 *
397 ********************************************************************/
398
399/*
400 *@@ nlsQueryCountrySettings:
401 * this returns the most frequently used country settings
402 * all at once into a COUNTRYSETTINGS structure (prfh.h).
403 * This data corresponds to the user settings in the
404 * WPS "Country" object (which writes the data in "PM_National"
405 * in OS2.INI).
406 *
407 * In case a key cannot be found, the following (English)
408 * default values are set:
409 * -- ulDateFormat = 0 (English date format, mm.dd.yyyy);
410 * -- ulTimeFormat = 0 (12-hour clock);
411 * -- cDateSep = '/' (date separator);
412 * -- cTimeSep = ':' (time separator);
413 * -- cDecimal = '.' (decimal separator).
414 * -- cThousands = ',' (thousands separator).
415 *
416 *@@added V0.9.0 [umoeller]
417 *@@changed V0.9.7 (2000-12-02) [umoeller]: added cDecimal
418 *@@changed V1.0.4 (2005-10-15) [bvl]: Added support for Locale object settings on MCP systems @@fixes 614
419 *@@changed V1.0.4 (2005-10-29) [pr]: Rewritten to prevent memory leaks and errors
420 *@@changed V1.0.5 (2006-05-29) [pr]: Read Country rather than Locale settings on Warp 4 FP13+ @@fixes 614
421 *@@changed WarpIN V1.0.15 (2007-03-26) [pr]: Rewritten to load UCONV/LIBUNI functions dynamically @@fixes 936
422 */
423
424VOID nlsQueryCountrySettings(PCOUNTRYSETTINGS pcs)
425{
426 if (pcs)
427 {
428 if ( (doshIsWarp4()==3)
429 && (!fUniResolved)
430 )
431 {
432 HMODULE hmodUCONV = NULLHANDLE,
433 hmodLIBUNI = NULLHANDLE;
434
435 fUniResolved = TRUE;
436 if ( (doshResolveImports("UCONV.DLL",
437 &hmodUCONV,
438 G_aResolveFromUCONV,
439 sizeof(G_aResolveFromUCONV) / sizeof(G_aResolveFromUCONV[0]))
440 == NO_ERROR)
441 && (doshResolveImports("LIBUNI.DLL",
442 &hmodLIBUNI,
443 G_aResolveFromLIBUNI,
444 sizeof(G_aResolveFromLIBUNI) / sizeof(G_aResolveFromLIBUNI[0]))
445 == NO_ERROR)
446 )
447 fUniOK = TRUE;
448 }
449
450 if ( (doshIsWarp4()==3) // V1.0.5 (2006-05-29) [pr]
451 && fUniOK
452 )
453 {
454 UconvObject uconv_object;
455
456 if (G_UniCreateUconvObject((UniChar *)L"",
457 &uconv_object) == ULS_SUCCESS)
458 {
459 LocaleObject locale_object;
460
461 if (G_UniCreateLocaleObject(UNI_UCS_STRING_POINTER,
462 (UniChar *)L"",
463 &locale_object) == ULS_SUCCESS)
464 {
465 int i;
466 struct LOCALE_ITEMLIST
467 {
468 LocaleItem lclItem;
469 PVOID vTarget;
470 int iType;
471 } itemList[] = {
472 { LOCI_iDate, &pcs->ulDateFormat, 1 },
473 { LOCI_iTime, &pcs->ulTimeFormat, 1 },
474 { LOCI_sDate, &pcs->cDateSep, 2 },
475 { LOCI_sTime, &pcs->cTimeSep, 2 },
476 { LOCI_sDecimal, &pcs->cDecimal, 2 },
477 { LOCI_sThousand, &pcs->cThousands, 2 }
478 };
479
480 for (i = 0;
481 i < sizeof(itemList) / sizeof(itemList[0]);
482 i++)
483 {
484 UniChar *pItem;
485
486 if (G_UniQueryLocaleItem(locale_object,
487 itemList[i].lclItem,
488 &pItem) == ULS_SUCCESS)
489 {
490 int iLen = G_UniStrlen(pItem) + 1;
491 PSZ pszResult = malloc(iLen);
492
493 if (G_UniStrFromUcs(uconv_object,
494 pszResult,
495 pItem,
496 iLen) == ULS_SUCCESS)
497 {
498 switch(itemList[i].iType)
499 {
500 case 1:
501 *((ULONG *) itemList[i].vTarget) = atol(pszResult);
502 break;
503
504 case 2:
505 *((CHAR *) itemList[i].vTarget) = pszResult[0];
506 break;
507 }
508 }
509
510 free(pszResult);
511 G_UniFreeMem(pItem);
512 }
513 }
514
515 G_UniFreeLocaleObject(locale_object);
516 }
517
518 G_UniFreeUconvObject(uconv_object);
519 }
520 }
521 else
522 {
523 pcs->ulDateFormat = PrfQueryProfileInt(HINI_USER,
524 (PSZ)PMINIAPP_NATIONAL,
525 "iDate",
526 0);
527 pcs->ulTimeFormat = PrfQueryProfileInt(HINI_USER,
528 (PSZ)PMINIAPP_NATIONAL,
529 "iTime",
530 0);
531 pcs->cDateSep = prfhQueryProfileChar(HINI_USER,
532 (PSZ)PMINIAPP_NATIONAL,
533 "sDate",
534 '/');
535 pcs->cTimeSep = prfhQueryProfileChar(HINI_USER,
536 (PSZ)PMINIAPP_NATIONAL,
537 "sTime",
538 ':');
539 pcs->cDecimal = prfhQueryProfileChar(HINI_USER,
540 (PSZ)PMINIAPP_NATIONAL,
541 "sDecimal",
542 '.');
543 pcs->cThousands = prfhQueryProfileChar(HINI_USER,
544 (PSZ)PMINIAPP_NATIONAL,
545 "sThousand",
546 ',');
547 }
548 }
549}
550
551/*
552 *@@ nlsThousandsULong:
553 * converts a ULONG into a decimal string, while
554 * inserting thousands separators into it. Specify
555 * the separator character in cThousands.
556 *
557 * Returns pszTarget so you can use it directly
558 * with sprintf and the "%s" flag.
559 *
560 * For cThousands, you should use the data in
561 * OS2.INI ("PM_National" application), which is
562 * always set according to the "Country" object.
563 * You can use nlsQueryCountrySettings to
564 * retrieve this setting.
565 *
566 * Use nlsThousandsDouble for "double" values.
567 *
568 *@@changed V0.9.20 (2002-07-03) [umoeller]: optimized
569 */
570
571PSZ nlsThousandsULong(PSZ pszTarget, // out: decimal as string
572 ULONG ul, // in: decimal to convert
573 CHAR cThousands) // in: separator char (e.g. '.')
574{
575 USHORT ust, uss, usc;
576 CHAR szTemp[40];
577 usc = sprintf(szTemp, "%lu", ul); // V0.9.20 (2002-07-03) [umoeller]
578
579 ust = 0;
580 // usc = strlen(szTemp);
581 for (uss = 0; uss < usc; uss++)
582 {
583 if (uss)
584 if (((usc - uss) % 3) == 0)
585 {
586 pszTarget[ust] = cThousands;
587 ust++;
588 }
589 pszTarget[ust++] = szTemp[uss];
590 }
591 pszTarget[ust] = '\0';
592
593 return pszTarget;
594}
595
596/*
597 * strhThousandsULong:
598 * wrapper around nlsThousandsULong for those
599 * who used the XFLDR.DLL export.
600 *
601 *added V0.9.16 (2001-10-11) [umoeller]
602 */
603
604PSZ APIENTRY strhThousandsULong(PSZ pszTarget, // out: decimal as string
605 ULONG ul, // in: decimal to convert
606 CHAR cThousands) // in: separator char (e.g. '.')
607{
608 return nlsThousandsULong(pszTarget, ul, cThousands);
609}
610
611/*
612 *@@ nlsThousandsDouble:
613 * like nlsThousandsULong, but for a "double"
614 * value. Note that after-comma values are truncated.
615 *
616 *@@changed V0.9.20 (2002-07-03) [umoeller]: optimized
617 *@@changed XWP V1.0.9 (2010-07-20) [pr]: added 0.5 to round
618 */
619
620PSZ nlsThousandsDouble(PSZ pszTarget,
621 double dbl,
622 CHAR cThousands)
623{
624 USHORT ust, uss, usc;
625 CHAR szTemp[40];
626 usc = sprintf(szTemp, "%.0f", floor(dbl + 0.5)); // V0.9.20 (2002-07-03) [umoeller]
627
628 ust = 0;
629 // usc = strlen(szTemp);
630 for (uss = 0; uss < usc; uss++)
631 {
632 if (uss)
633 if (((usc - uss) % 3) == 0)
634 {
635 pszTarget[ust] = cThousands;
636 ust++;
637 }
638 pszTarget[ust] = szTemp[uss];
639 ust++;
640 }
641 pszTarget[ust] = '\0';
642
643 return pszTarget;
644}
645
646/*
647 *@@ nlsVariableDouble:
648 * like nlsThousandsULong, but for a "double" value, and
649 * with a variable number of decimal places depending on the
650 * size of the quantity.
651 *
652 *@@added V0.9.6 (2000-11-12) [pr]
653 *@@changed V0.9.20 (2002-07-03) [umoeller]: now using PCSZ pcszUnits
654 */
655
656PSZ nlsVariableDouble(PSZ pszTarget,
657 double dbl,
658 PCSZ pcszUnits,
659 CHAR cThousands)
660{
661 if (dbl < 100.0)
662 sprintf(pszTarget, "%.2f%s", dbl, pcszUnits);
663 else
664 if (dbl < 1000.0)
665 sprintf(pszTarget, "%.1f%s", dbl, pcszUnits);
666 else
667 strcat(nlsThousandsDouble(pszTarget, dbl, cThousands),
668 pcszUnits);
669
670 return pszTarget;
671}
672
673/*
674 *@@ nlsFileDate:
675 * converts file date data to a string (to pszBuf).
676 * You can pass any FDATE structure to this function,
677 * which are returned in those FILEFINDBUF* or
678 * FILESTATUS* structs by the Dos* functions.
679 *
680 * ulDateFormat is the PM setting for the date format,
681 * as set in the "Country" object, and can be queried using
682 + PrfQueryProfileInt(HINI_USER, "PM_National", "iDate", 0);
683 *
684 * meaning:
685 * -- 0 mm.dd.yyyy (English)
686 * -- 1 dd.mm.yyyy (e.g. German)
687 * -- 2 yyyy.mm.dd (Japanese, ISO)
688 * -- 3 yyyy.dd.mm
689 *
690 * cDateSep is used as a date separator (e.g. '.').
691 * This can be queried using:
692 + prfhQueryProfileChar(HINI_USER, "PM_National", "sDate", '/');
693 *
694 * Alternatively, you can query all the country settings
695 * at once using nlsQueryCountrySettings (prfh.c).
696 *
697 *@@changed V0.9.0 (99-11-07) [umoeller]: now calling nlsDateTime
698 */
699
700VOID nlsFileDate(PSZ pszBuf, // out: string returned
701 FDATE *pfDate, // in: date information
702 ULONG ulDateFormat, // in: date format (0-3)
703 CHAR cDateSep) // in: date separator (e.g. '.')
704{
705 DATETIME dt;
706 dt.day = pfDate->day;
707 dt.month = pfDate->month;
708 dt.year = pfDate->year + 1980;
709
710 nlsDateTime(pszBuf,
711 NULL, // no time
712 &dt,
713 ulDateFormat,
714 cDateSep,
715 0, 0); // no time
716}
717
718/*
719 *@@ nlsFileTime:
720 * converts file time data to a string (to pszBuf).
721 * You can pass any FTIME structure to this function,
722 * which are returned in those FILEFINDBUF* or
723 * FILESTATUS* structs by the Dos* functions.
724 *
725 * ulTimeFormat is the PM setting for the time format,
726 * as set in the "Country" object, and can be queried using
727 + PrfQueryProfileInt(HINI_USER, "PM_National", "iTime", 0);
728 * meaning:
729 * -- 0 12-hour clock
730 * -- >0 24-hour clock
731 *
732 * cDateSep is used as a time separator (e.g. ':').
733 * This can be queried using:
734 + prfhQueryProfileChar(HINI_USER, "PM_National", "sTime", ':');
735 *
736 * Alternatively, you can query all the country settings
737 * at once using nlsQueryCountrySettings (prfh.c).
738 *
739 *@@changed V0.8.5 (99-03-15) [umoeller]: fixed 12-hour crash
740 *@@changed V0.9.0 (99-11-07) [umoeller]: now calling nlsDateTime
741 */
742
743VOID nlsFileTime(PSZ pszBuf, // out: string returned
744 FTIME *pfTime, // in: time information
745 ULONG ulTimeFormat, // in: 24-hour time format (0 or 1)
746 CHAR cTimeSep) // in: time separator (e.g. ':')
747{
748 DATETIME dt;
749 dt.hours = pfTime->hours;
750 dt.minutes = pfTime->minutes;
751 dt.seconds = pfTime->twosecs * 2;
752
753 nlsDateTime(NULL, // no date
754 pszBuf,
755 &dt,
756 0, 0, // no date
757 ulTimeFormat,
758 cTimeSep);
759}
760
761/*
762 *@@ nlsDateTime:
763 * converts Control Program DATETIME info
764 * into two strings. See nlsFileDate and nlsFileTime
765 * for more detailed parameter descriptions.
766 *
767 *@@added V0.9.0 (99-11-07) [umoeller]
768 *@@changed V0.9.16 (2001-12-05) [pr]: fixed AM/PM hour bug
769 *@@changed V0.9.18 (2002-02-13) [umoeller]: fixed AM/PM hour bug fix
770 */
771
772VOID nlsDateTime(PSZ pszDate, // out: date string returned (can be NULL)
773 PSZ pszTime, // out: time string returned (can be NULL)
774 DATETIME *pDateTime, // in: date/time information
775 ULONG ulDateFormat, // in: date format (0-3); see nlsFileDate
776 CHAR cDateSep, // in: date separator (e.g. '.')
777 ULONG ulTimeFormat, // in: 24-hour time format (0 or 1); see nlsFileTime
778 CHAR cTimeSep) // in: time separator (e.g. ':')
779{
780 if (pszDate)
781 {
782 switch (ulDateFormat)
783 {
784 case 0: // mm.dd.yyyy (English)
785 sprintf(pszDate, "%02d%c%02d%c%04d",
786 pDateTime->month,
787 cDateSep,
788 pDateTime->day,
789 cDateSep,
790 pDateTime->year);
791 break;
792
793 case 1: // dd.mm.yyyy (e.g. German)
794 sprintf(pszDate, "%02d%c%02d%c%04d",
795 pDateTime->day,
796 cDateSep,
797 pDateTime->month,
798 cDateSep,
799 pDateTime->year);
800 break;
801
802 case 2: // yyyy.mm.dd (Japanese)
803 sprintf(pszDate, "%04d%c%02d%c%02d",
804 pDateTime->year,
805 cDateSep,
806 pDateTime->month,
807 cDateSep,
808 pDateTime->day);
809 break;
810
811 default: // yyyy.dd.mm
812 sprintf(pszDate, "%04d%c%02d%c%02d",
813 pDateTime->year,
814 cDateSep,
815 pDateTime->day,
816 cDateSep,
817 pDateTime->month);
818 break;
819 }
820 }
821
822 if (pszTime)
823 {
824 if (ulTimeFormat == 0)
825 {
826 // for 12-hour clock, we need additional INI data
827 CHAR szAMPM[10] = "err";
828
829 if (pDateTime->hours >= 12) // V0.9.16 (2001-12-05) [pr] if (pDateTime->hours > 12)
830 {
831 // yeah cool Paul, now we get 00:20 PM if it's 20 past noon
832 // V0.9.18 (2002-02-13) [umoeller]
833 ULONG ulHours;
834 if (!(ulHours = pDateTime->hours % 12))
835 ulHours = 12;
836
837 // >= 12h: PM.
838 PrfQueryProfileString(HINI_USER,
839 "PM_National",
840 "s2359", // key
841 "PM", // default
842 szAMPM, sizeof(szAMPM)-1);
843 sprintf(pszTime, "%02d%c%02d%c%02d %s",
844 // leave 12 == 12 (not 0)
845 ulHours,
846 cTimeSep,
847 pDateTime->minutes,
848 cTimeSep,
849 pDateTime->seconds,
850 szAMPM);
851 }
852 else
853 {
854 // < 12h: AM
855 PrfQueryProfileString(HINI_USER,
856 "PM_National",
857 "s1159", // key
858 "AM", // default
859 szAMPM, sizeof(szAMPM)-1);
860 sprintf(pszTime, "%02d%c%02d%c%02d %s",
861 pDateTime->hours,
862 cTimeSep,
863 pDateTime->minutes,
864 cTimeSep,
865 pDateTime->seconds,
866 szAMPM);
867 }
868 }
869 else
870 // 24-hour clock
871 sprintf(pszTime, "%02d%c%02d%c%02d",
872 pDateTime->hours,
873 cTimeSep,
874 pDateTime->minutes,
875 cTimeSep,
876 pDateTime->seconds);
877 }
878}
879
880/*
881 * strhDateTime:
882 * wrapper around nlsDateTime for those who used
883 * the XFLDR.DLL export.
884 */
885
886VOID APIENTRY strhDateTime(PSZ pszDate, // out: date string returned (can be NULL)
887 PSZ pszTime, // out: time string returned (can be NULL)
888 DATETIME *pDateTime, // in: date/time information
889 ULONG ulDateFormat, // in: date format (0-3); see nlsFileDate
890 CHAR cDateSep, // in: date separator (e.g. '.')
891 ULONG ulTimeFormat, // in: 24-hour time format (0 or 1); see nlsFileTime
892 CHAR cTimeSep) // in: time separator (e.g. ':')
893{
894 nlsDateTime(pszDate,
895 pszTime,
896 pDateTime,
897 ulDateFormat,
898 cDateSep,
899 ulTimeFormat,
900 cTimeSep);
901}
902
903CHAR G_szUpperMap[257];
904BOOL G_fUpperMapInited = FALSE;
905
906/*
907 *@@ InitUpperMap:
908 * initializes the case map for nlsUpper.
909 *
910 *@@added V0.9.20 (2002-07-25) [umoeller]
911 */
912
913STATIC VOID InitUpperMap(VOID)
914{
915 ULONG ul;
916 COUNTRYCODE cc;
917 BOOL fDBCS = nlsDBCS();
918
919 for (ul = 0;
920 ul < 256;
921 ++ul)
922 {
923 G_szUpperMap[ul] = (CHAR)ul;
924
925 if ( (fDBCS)
926 && (G_afLeadByte[ul] != TYPE_SBCS)
927 )
928 G_szUpperMap[ul] = ' ';
929 }
930
931 G_szUpperMap[256] = '\0';
932
933 cc.country = 0; // use system country code
934 cc.codepage = 0; // use process default codepage
935 DosMapCase(255,
936 &cc,
937 G_szUpperMap + 1);
938
939 G_fUpperMapInited = TRUE;
940}
941
942/*
943 *@@ nlsUpper:
944 * quick hack for upper-casing a string.
945 *
946 * This now returns the length of the given string always
947 * (V0.9.20).
948 *
949 * Remarks:
950 *
951 * -- On the first call, we build a case map table by
952 * calling DosMapCase with the default system country
953 * code and the process's codepage. Since DosMapCase
954 * is terribly slow with all the 16-bit thunking
955 * involved, we can then use our table without having
956 * to use the API ever again.
957 *
958 * -- As a result, if the process codepage changes for
959 * any reason, this function will not pick up the
960 * change.
961 *
962 * -- This has provisions for DBCS, which hopefully
963 * work.
964 *
965 *@@added V0.9.16 (2001-10-25) [umoeller]
966 *@@changed V0.9.20 (2002-07-25) [umoeller]: speedup, changed prototype
967 *@@changed XWP V1.0.8 (2008-05-25) [pr]: rewritten for correct DBCS operation @@fixes 1070
968 */
969
970ULONG nlsUpper(PSZ psz) // in/out: string
971{
972 BOOL bDBCSType = TYPE_SBCS;
973 ULONG ul = 0;
974 PSZ p = psz;
975
976 if (!G_fUpperMapInited)
977 InitUpperMap();
978
979 for (; p && *p; p++, ul++)
980 {
981 switch(bDBCSType)
982 {
983 case TYPE_SBCS:
984 case TYPE_DBCS_2ND:
985 bDBCSType = G_afLeadByte[*p];
986 break;
987
988 case TYPE_DBCS_1ST :
989 bDBCSType = TYPE_DBCS_2ND;
990 break;
991 }
992
993 if (bDBCSType == TYPE_SBCS)
994 *p = G_szUpperMap[*p];
995 }
996
997 return ul;
998}
999
Note: See TracBrowser for help on using the repository browser.