source: trunk/src/helpers/nls.c@ 391

Last change on this file since 391 was 390, checked in by pr, 14 years ago

Add nlsThousandsLong to fix signed/unsigned display error.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 37.4 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-2011 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 *@@ nlsQueryCodepage:
141 * returns the current process codepage as a ULONG.
142 *
143 *@@added V1.0.2 (2003-02-07) [umoeller]
144 */
145
146ULONG nlsQueryCodepage(VOID)
147{
148 ULONG acp[8];
149 ULONG cb = 0;
150 if (DosQueryCp(sizeof(acp),
151 acp,
152 &cb))
153 return 437; // I think this is still the system default
154
155 return acp[0];
156}
157
158/*
159 *@@ nlsDBCS:
160 * returns TRUE if the system is currently using DBCS.
161 *
162 *@@added V0.9.19 (2002-06-13) [umoeller]
163 *@@changed V0.9.20 (2002-07-03) [umoeller]: fixed, this never worked
164 */
165
166BOOL nlsDBCS(VOID)
167{
168 APIRET arc;
169
170 if (G_fDBCS != 2)
171 // already queried:
172 return G_fDBCS;
173
174 // V0.9.20 (2002-07-03) [umoeller]
175 // assume a non-DBCS system UNLESS the below
176 // loop gives us something meaningful; even
177 // on non-DBCS systems like mine, DosQueryDBCSEnv
178 // does not return an error
179 G_fDBCS = FALSE;
180
181 if (!(arc = DosQueryDBCSEnv(8 * sizeof(DBCSVECTOR),
182 &G_cc,
183 (PCHAR)G_aDBCSVector)))
184 {
185 int i;
186 for (i = 0;
187 i < 8;
188 ++i)
189 {
190 if ( (G_aDBCSVector[i].bLow)
191 && (G_aDBCSVector[i].bHigh)
192 )
193 {
194 int n;
195 for (n = G_aDBCSVector[i].bLow;
196 n <= G_aDBCSVector[i].bHigh;
197 ++n)
198 G_afLeadByte[n] = TRUE;
199
200 G_fDBCS = TRUE;
201 }
202 else
203 break;
204 }
205 }
206
207 return G_fDBCS;
208}
209
210/*
211 *@@ nlsQueryDBCSChar:
212 * returns the type of the DBCS character with
213 * the given index. Note that the index is the
214 * DBCS character index, not just the array
215 * index into the CHAR array.
216 *
217 * Returns:
218 *
219 * -- TYPE_SBCS: ulOfs is single byte.
220 *
221 * -- TYPE_DBCS_1ST: ulOfs is a double-byte lead char.
222 *
223 * -- TYPE_DBCS_2ND: ulOfs is a double-byte trail char.
224 *
225 * Preconditions:
226 *
227 * -- nlsDBCS must have been called to initialize our
228 * globals, and must have returned TRUE.
229 *
230 *@@added V0.9.19 (2002-06-13) [umoeller]
231 */
232
233ULONG nlsQueryDBCSChar(PCSZ pcszString,
234 ULONG ulOfs)
235
236{
237 ULONG ulDBCSType = TYPE_SBCS;
238 ULONG i;
239
240 for (i = 0;
241 i <= ulOfs;
242 ++i)
243 {
244 switch (ulDBCSType)
245 {
246 case TYPE_SBCS:
247 case TYPE_DBCS_2ND:
248 ulDBCSType = G_afLeadByte[pcszString[i]];
249 break;
250
251 case TYPE_DBCS_1ST :
252 ulDBCSType = TYPE_DBCS_2ND;
253 break;
254 }
255 }
256
257 return ulDBCSType;
258}
259
260/*
261 *@@ nlschr:
262 * replacement for strchr with DBCS support.
263 *
264 * If the system is not running with DBCS,
265 * this calls plain strchr automatically.
266 *
267 *@@added V0.9.19 (2002-06-13) [umoeller]
268 *@@changed V0.9.20 (2002-07-22) [umoeller]: optimized
269 *@@changed V0.9.20 (2002-07-22) [lafaix]: optimized
270 */
271
272PSZ nlschr(PCSZ p, char c)
273{
274 PCSZ p2;
275 ULONG ulDBCSType = TYPE_SBCS;
276
277 if (!nlsDBCS())
278 // not DBCS:
279 return strchr(p, c);
280
281 // we're on DBCS:
282
283 // we can't find c if it is a leading byte
284 if (G_afLeadByte[c] != TYPE_SBCS)
285 return NULL;
286
287 for (p2 = p;
288 *p2;
289 ++p2)
290 {
291 // check _previous_ DBCS type and refresh
292 // DBCS type accordingly
293 switch (ulDBCSType)
294 {
295 case TYPE_SBCS:
296 case TYPE_DBCS_2ND:
297 ulDBCSType = G_afLeadByte[*p2];
298 // we can safely do the test here (and skip rechecking
299 // the type) because c can't possibly be a leading byte
300 // V0.9.20 (2002-07-22) [lafaix]
301 if (*p2 == c)
302 return (PSZ)p2;
303 break;
304
305 case TYPE_DBCS_1ST :
306 ulDBCSType = TYPE_DBCS_2ND;
307 break;
308 }
309 }
310
311 /* old code V0.9.20 (2002-07-22) [umoeller]
312 // we're on DBCS:
313 for (p2 = p;
314 *p2;
315 ++p2)
316 {
317 if (*p2 == c)
318 {
319 // match: return this only if it's SBCS;
320 // if it's a DBCS lead char, skip it
321 switch (ulDBCS = nlsQueryDBCSChar(p, p2 - p))
322 {
323 case TYPE_SBCS:
324 return (PSZ)p2;
325
326 case TYPE_DBCS_1ST:
327 ++p2;
328 }
329 }
330 }
331 */
332
333 return NULL;
334}
335
336/*
337 *@@ nlsrchr:
338 * replacement for strrchr with DBCS support.
339 *
340 * If the system is not running with DBCS,
341 * this calls plain strrchr automatically.
342 *
343 *@@added V0.9.19 (2002-06-13) [umoeller]
344 *@@changed V0.9.20 (2002-07-22) [lafaix]: optimized
345 */
346
347PSZ nlsrchr(PCSZ p, char c)
348{
349 PCSZ p2,
350 pLast = NULL;
351 ULONG ulDBCSType = TYPE_SBCS;
352
353 if (!nlsDBCS())
354 // not DBCS:
355 return strrchr(p, c);
356
357 // we're on DBCS:
358
359 // we can't find c if it is a leading byte
360 if (G_afLeadByte[c] != TYPE_SBCS)
361 return NULL;
362
363 for (p2 = p;
364 *p2;
365 ++p2)
366 {
367 // check _previous_ DBCS type and refresh
368 // DBCS type accordingly
369 switch (ulDBCSType)
370 {
371 case TYPE_SBCS:
372 case TYPE_DBCS_2ND:
373 ulDBCSType = G_afLeadByte[*p2];
374 if (*p2 == c)
375 pLast = p2;
376 break;
377
378 case TYPE_DBCS_1ST :
379 ulDBCSType = TYPE_DBCS_2ND;
380 break;
381 }
382 }
383
384 return (PSZ)pLast;
385
386 // old code V0.9.20 (2002-07-22) [lafaix]
387 /*
388 ulLength = strlen(p);
389 for (p2 = p + ulLength - 1;
390 p2 >= p;
391 --p2)
392 {
393 if (*p2 == c)
394 {
395 // match: return this only if it's SBCS;
396 // if it's a DBCS trail char, skip it
397 switch (ulDBCS = nlsQueryDBCSChar(p, p2 - p))
398 {
399 case TYPE_SBCS:
400 return (PSZ)p2;
401
402 case TYPE_DBCS_2ND:
403 --p2;
404 }
405 }
406 }
407
408 return NULL;
409 */
410}
411
412/* ******************************************************************
413 *
414 * Country-dependent formatting
415 *
416 ********************************************************************/
417
418/*
419 *@@ nlsGetAMPM:
420 *
421 *@@added V1.0.1 (2003-01-17) [umoeller]
422 *@@changed V1.0.4 (2005-10-15) [pr]: Added support for Locale object settings on MCP systems @@fixes 614
423 *@@changed V1.0.5 (2006-05-29) [pr]: Read Country rather than Locale settings on Warp 4 FP13+ @@fixes 614
424 *@@changed WarpIN V1.0.15 (2007-03-26) [pr]: Rewritten to load UCONV/LIBUNI functions dynamically @@fixes 936
425 */
426
427VOID nlsGetAMPM(PCOUNTRYAMPM pampm)
428{
429 if ( (doshIsWarp4()==3)
430 && (!fUniResolved)
431 )
432 {
433 HMODULE hmodUCONV = NULLHANDLE,
434 hmodLIBUNI = NULLHANDLE;
435
436 fUniResolved = TRUE;
437 if ( (doshResolveImports("UCONV.DLL",
438 &hmodUCONV,
439 G_aResolveFromUCONV,
440 sizeof(G_aResolveFromUCONV) / sizeof(G_aResolveFromUCONV[0]))
441 == NO_ERROR)
442 && (doshResolveImports("LIBUNI.DLL",
443 &hmodLIBUNI,
444 G_aResolveFromLIBUNI,
445 sizeof(G_aResolveFromLIBUNI) / sizeof(G_aResolveFromLIBUNI[0]))
446 == NO_ERROR)
447 )
448 fUniOK = TRUE;
449 }
450
451 if ( (doshIsWarp4()==3) // V1.0.5 (2006-05-29) [pr]
452 && fUniOK
453 )
454 {
455 UconvObject uconv_object;
456
457 if (G_UniCreateUconvObject((UniChar *)L"",
458 &uconv_object) == ULS_SUCCESS)
459 {
460 LocaleObject locale_object;
461
462 if (G_UniCreateLocaleObject(UNI_UCS_STRING_POINTER,
463 (UniChar *)L"",
464 &locale_object) == ULS_SUCCESS)
465 {
466 int i;
467 struct LOCALE_ITEMLIST
468 {
469 LocaleItem lclItem;
470 PVOID vTarget;
471 int iType;
472 } itemList[] = {
473 { LOCI_s2359, &pampm->sz2359, 3 },
474 { LOCI_s1159, &pampm->sz1159, 3 }
475 };
476
477 for (i = 0;
478 i < sizeof(itemList) / sizeof(itemList[0]);
479 i++)
480 {
481 UniChar *pItem;
482
483 if (G_UniQueryLocaleItem(locale_object,
484 itemList[i].lclItem,
485 &pItem) == ULS_SUCCESS)
486 {
487 int iLen = G_UniStrlen(pItem) + 1;
488 PSZ pszResult = malloc(iLen);
489
490 if (G_UniStrFromUcs(uconv_object,
491 pszResult,
492 pItem,
493 iLen) == ULS_SUCCESS)
494 {
495 switch(itemList[i].iType)
496 {
497 case 1:
498 *((ULONG *) itemList[i].vTarget) = atol(pszResult);
499 break;
500
501 case 2:
502 *((CHAR *) itemList[i].vTarget) = pszResult[0];
503 break;
504
505 case 3:
506 strcpy (itemList[i].vTarget, pszResult);
507 break;
508 }
509 }
510
511 free(pszResult);
512 G_UniFreeMem(pItem);
513 }
514 }
515
516 G_UniFreeLocaleObject(locale_object);
517 }
518
519 G_UniFreeUconvObject(uconv_object);
520 }
521 }
522 else
523 {
524 PrfQueryProfileString(HINI_USER,
525 "PM_National",
526 "s2359", // key
527 "PM", // default
528 pampm->sz2359,
529 sizeof(pampm->sz2359));
530
531 PrfQueryProfileString(HINI_USER,
532 "PM_National",
533 "s1159", // key
534 "AM", // default
535 pampm->sz1159,
536 sizeof(pampm->sz1159));
537 }
538}
539
540/*
541 *@@ nlsQueryCountrySettings:
542 * this returns the most frequently used country settings
543 * all at once into a COUNTRYSETTINGS structure (prfh.h).
544 * This data corresponds to the user settings in the
545 * WPS "Country" object (which writes the data in "PM_National"
546 * in OS2.INI).
547 *
548 * In case a key cannot be found, the following (English)
549 * default values are set:
550 * -- ulDateFormat = 0 (English date format, mm.dd.yyyy);
551 * -- ulTimeFormat = 0 (12-hour clock);
552 * -- cDateSep = '/' (date separator);
553 * -- cTimeSep = ':' (time separator);
554 * -- cDecimal = '.' (decimal separator).
555 * -- cThousands = ',' (thousands separator).
556 *
557 *@@added V0.9.0 [umoeller]
558 *@@changed V0.9.7 (2000-12-02) [umoeller]: added cDecimal
559 *@@changed V1.0.4 (2005-10-15) [bvl]: Added support for Locale object settings on MCP systems @@fixes 614
560 *@@changed V1.0.4 (2005-10-29) [pr]: Rewritten to prevent memory leaks and errors
561 *@@changed V1.0.5 (2006-05-29) [pr]: Read Country rather than Locale settings on Warp 4 FP13+ @@fixes 614
562 *@@changed WarpIN V1.0.15 (2007-03-26) [pr]: Rewritten to load UCONV/LIBUNI functions dynamically @@fixes 936
563 */
564
565VOID nlsQueryCountrySettings(PCOUNTRYSETTINGS2 pcs2)
566{
567 if (pcs2)
568 {
569 PCOUNTRYSETTINGS pcs = &pcs2->cs;
570
571 if ( (doshIsWarp4()==3)
572 && (!fUniResolved)
573 )
574 {
575 HMODULE hmodUCONV = NULLHANDLE,
576 hmodLIBUNI = NULLHANDLE;
577
578 fUniResolved = TRUE;
579 if ( (doshResolveImports("UCONV.DLL",
580 &hmodUCONV,
581 G_aResolveFromUCONV,
582 sizeof(G_aResolveFromUCONV) / sizeof(G_aResolveFromUCONV[0]))
583 == NO_ERROR)
584 && (doshResolveImports("LIBUNI.DLL",
585 &hmodLIBUNI,
586 G_aResolveFromLIBUNI,
587 sizeof(G_aResolveFromLIBUNI) / sizeof(G_aResolveFromLIBUNI[0]))
588 == NO_ERROR)
589 )
590 fUniOK = TRUE;
591 }
592
593 if ( (doshIsWarp4()==3) // V1.0.5 (2006-05-29) [pr]
594 && fUniOK
595 )
596 {
597 UconvObject uconv_object;
598
599 if (G_UniCreateUconvObject((UniChar *)L"",
600 &uconv_object) == ULS_SUCCESS)
601 {
602 LocaleObject locale_object;
603
604 if (G_UniCreateLocaleObject(UNI_UCS_STRING_POINTER,
605 (UniChar *)L"",
606 &locale_object) == ULS_SUCCESS)
607 {
608 int i;
609 struct LOCALE_ITEMLIST
610 {
611 LocaleItem lclItem;
612 PVOID vTarget;
613 int iType;
614 } itemList[] = {
615 { LOCI_iDate, &pcs->ulDateFormat, 1 },
616 { LOCI_iTime, &pcs->ulTimeFormat, 1 },
617 { LOCI_sDate, &pcs->cDateSep, 2 },
618 { LOCI_sTime, &pcs->cTimeSep, 2 },
619 { LOCI_sDecimal, &pcs->cDecimal, 2 },
620 { LOCI_sThousand, &pcs->cThousands, 2 }
621 };
622
623 for (i = 0;
624 i < sizeof(itemList) / sizeof(itemList[0]);
625 i++)
626 {
627 UniChar *pItem;
628
629 if (G_UniQueryLocaleItem(locale_object,
630 itemList[i].lclItem,
631 &pItem) == ULS_SUCCESS)
632 {
633 int iLen = G_UniStrlen(pItem) + 1;
634 PSZ pszResult = malloc(iLen);
635
636 if (G_UniStrFromUcs(uconv_object,
637 pszResult,
638 pItem,
639 iLen) == ULS_SUCCESS)
640 {
641 switch(itemList[i].iType)
642 {
643 case 1:
644 *((ULONG *) itemList[i].vTarget) = atol(pszResult);
645 break;
646
647 case 2:
648 *((CHAR *) itemList[i].vTarget) = pszResult[0];
649 break;
650
651 case 3:
652 strcpy (itemList[i].vTarget, pszResult);
653 break;
654 }
655 }
656
657 free(pszResult);
658 G_UniFreeMem(pItem);
659 }
660 }
661
662 G_UniFreeLocaleObject(locale_object);
663 }
664
665 G_UniFreeUconvObject(uconv_object);
666 }
667 }
668 else
669 {
670 pcs->ulDateFormat = PrfQueryProfileInt(HINI_USER,
671 (PSZ)PMINIAPP_NATIONAL,
672 "iDate",
673 0);
674 pcs->ulTimeFormat = PrfQueryProfileInt(HINI_USER,
675 (PSZ)PMINIAPP_NATIONAL,
676 "iTime",
677 0);
678 pcs->cDateSep = prfhQueryProfileChar(HINI_USER,
679 (PSZ)PMINIAPP_NATIONAL,
680 "sDate",
681 '/');
682 pcs->cTimeSep = prfhQueryProfileChar(HINI_USER,
683 (PSZ)PMINIAPP_NATIONAL,
684 "sTime",
685 ':');
686 pcs->cDecimal = prfhQueryProfileChar(HINI_USER,
687 (PSZ)PMINIAPP_NATIONAL,
688 "sDecimal",
689 '.');
690 pcs->cThousands = prfhQueryProfileChar(HINI_USER,
691 (PSZ)PMINIAPP_NATIONAL,
692 "sThousand",
693 ',');
694 }
695
696 nlsGetAMPM(&pcs2->ampm);
697 }
698}
699
700/*
701 *@@ nlsThousandsULong:
702 * converts a ULONG into a decimal string, while
703 * inserting thousands separators into it. Specify
704 * the separator character in cThousands.
705 *
706 * Returns pszTarget so you can use it directly
707 * with sprintf and the "%s" flag.
708 *
709 * For cThousands, you should use the data in
710 * OS2.INI ("PM_National" application), which is
711 * always set according to the "Country" object.
712 * You can use nlsQueryCountrySettings to
713 * retrieve this setting.
714 *
715 * Use nlsThousandsDouble for "double" values.
716 *
717 *@@changed V0.9.20 (2002-07-03) [umoeller]: optimized
718 */
719
720PSZ nlsThousandsULong(PSZ pszTarget, // out: decimal as string
721 ULONG ul, // in: decimal to convert
722 CHAR cThousands) // in: separator char (e.g. '.')
723{
724 CHAR szTemp[30];
725 USHORT ust = 0,
726 uss,
727 usLen = sprintf(szTemp, "%lu", ul); // V0.9.20 (2002-07-03) [umoeller]
728
729 for (uss = 0;
730 uss < usLen;
731 uss++)
732 {
733 if (uss)
734 if (((usLen - uss) % 3) == 0)
735 {
736 pszTarget[ust] = cThousands;
737 ust++;
738 }
739
740 pszTarget[ust++] = szTemp[uss];
741 }
742
743 pszTarget[ust] = '\0';
744
745 return pszTarget;
746}
747
748/*
749 * strhThousandsULong:
750 * wrapper around nlsThousandsULong for those
751 * who used the XFLDR.DLL export.
752 *
753 *added V0.9.16 (2001-10-11) [umoeller]
754 */
755
756PSZ APIENTRY strhThousandsULong(PSZ pszTarget, // out: decimal as string
757 ULONG ul, // in: decimal to convert
758 CHAR cThousands) // in: separator char (e.g. '.')
759{
760 return nlsThousandsULong(pszTarget, ul, cThousands);
761}
762
763/*
764 *@@ nlsThousandsLong:
765 * signed version of above.
766 *
767 *@@added WarpIn V1.0.20 (2011-05-30) [pr]
768 */
769
770PSZ nlsThousandsLong(PSZ pszTarget, // out: decimal as string
771 LONG l, // in: decimal to convert
772 CHAR cThousands) // in: separator char (e.g. '.')
773{
774 CHAR szTemp[30];
775 USHORT ust = 0,
776 uss,
777 usLen = sprintf(szTemp, "%ld", l);
778
779 for (uss = 0;
780 uss < usLen;
781 uss++)
782 {
783 if (uss)
784 if (((usLen - uss) % 3) == 0)
785 {
786 pszTarget[ust] = cThousands;
787 ust++;
788 }
789
790 pszTarget[ust++] = szTemp[uss];
791 }
792
793 pszTarget[ust] = '\0';
794
795 return pszTarget;
796}
797
798/*
799 *@@ nlsThousandsDouble:
800 * like nlsThousandsULong, but for a "double"
801 * value. Note that after-comma values are truncated.
802 *
803 *@@changed V0.9.20 (2002-07-03) [umoeller]: optimized
804 *@@changed XWP V1.0.9 (2010-07-20) [pr]: added 0.5 to round
805 */
806
807PSZ nlsThousandsDouble(PSZ pszTarget,
808 double dbl,
809 CHAR cThousands)
810{
811 USHORT ust, uss, usLen;
812 CHAR szTemp[40];
813 usLen = sprintf(szTemp, "%.0f", floor(dbl + 0.5)); // V0.9.20 (2002-07-03) [umoeller]
814
815 ust = 0;
816 for (uss = 0; uss < usLen; uss++)
817 {
818 if (uss)
819 if (((usLen - uss) % 3) == 0)
820 {
821 pszTarget[ust] = cThousands;
822 ust++;
823 }
824 pszTarget[ust] = szTemp[uss];
825 ust++;
826 }
827 pszTarget[ust] = '\0';
828
829 return pszTarget;
830}
831
832/*
833 *@@ nlsVariableDouble:
834 * like nlsThousandsULong, but for a "double" value, and
835 * with a variable number of decimal places depending on the
836 * size of the quantity.
837 *
838 *@@added V0.9.6 (2000-11-12) [pr]
839 *@@changed V0.9.20 (2002-07-03) [umoeller]: now using PCSZ pcszUnits
840 */
841
842PSZ nlsVariableDouble(PSZ pszTarget,
843 double dbl,
844 PCSZ pcszUnits,
845 CHAR cThousands)
846{
847 if (dbl < 100.0)
848 sprintf(pszTarget, "%.2f%s", dbl, pcszUnits);
849 else
850 if (dbl < 1000.0)
851 sprintf(pszTarget, "%.1f%s", dbl, pcszUnits);
852 else
853 strcat(nlsThousandsDouble(pszTarget, dbl, cThousands),
854 pcszUnits);
855
856 return pszTarget;
857}
858
859/*
860 *@@ nlsFileDate:
861 * converts file date data to a string (to pszBuf).
862 * You can pass any FDATE structure to this function,
863 * which are returned in those FILEFINDBUF* or
864 * FILESTATUS* structs by the Dos* functions.
865 *
866 * ulDateFormat is the PM setting for the date format,
867 * as set in the "Country" object, and can be queried using
868 + PrfQueryProfileInt(HINI_USER, "PM_National", "iDate", 0);
869 *
870 * meaning:
871 * -- 0 mm.dd.yyyy (English)
872 * -- 1 dd.mm.yyyy (e.g. German)
873 * -- 2 yyyy.mm.dd (Japanese, ISO)
874 * -- 3 yyyy.dd.mm
875 *
876 * cDateSep is used as a date separator (e.g. '.').
877 * This can be queried using:
878 + prfhQueryProfileChar(HINI_USER, "PM_National", "sDate", '/');
879 *
880 * Alternatively, you can query all the country settings
881 * at once using nlsQueryCountrySettings (prfh.c).
882 *
883 *@@changed V0.9.0 (99-11-07) [umoeller]: now calling nlsDateTime
884 *@@changed V1.0.1 (2003-01-17) [umoeller]: prototype changed for optimization, this was not exported
885 */
886
887VOID nlsFileDate(PSZ pszBuf, // out: string returned
888 const FDATE *pfDate, // in: date information
889 const COUNTRYSETTINGS2 *pcs)
890{
891 nlsDate(pcs,
892 pszBuf,
893 pfDate->year + 1980,
894 pfDate->month,
895 pfDate->day);
896}
897
898/*
899 *@@ nlsFileTime:
900 * converts file time data to a string (to pszBuf).
901 * You can pass any FTIME structure to this function,
902 * which are returned in those FILEFINDBUF* or
903 * FILESTATUS* structs by the Dos* functions.
904 *
905 * ulTimeFormat is the PM setting for the time format,
906 * as set in the "Country" object, and can be queried using
907 + PrfQueryProfileInt(HINI_USER, "PM_National", "iTime", 0);
908 * meaning:
909 * -- 0 12-hour clock
910 * -- >0 24-hour clock
911 *
912 * cDateSep is used as a time separator (e.g. ':').
913 * This can be queried using:
914 + prfhQueryProfileChar(HINI_USER, "PM_National", "sTime", ':');
915 *
916 * Alternatively, you can query all the country settings
917 * at once using nlsQueryCountrySettings (prfh.c).
918 *
919 *@@changed V0.8.5 (99-03-15) [umoeller]: fixed 12-hour crash
920 *@@changed V0.9.0 (99-11-07) [umoeller]: now calling nlsDateTime
921 *@@changed V1.0.1 (2003-01-17) [umoeller]: prototype changed for optimization, this was not exported
922 */
923
924VOID nlsFileTime(PSZ pszBuf, // out: string returned
925 const FTIME *pfTime, // in: time information
926 const COUNTRYSETTINGS2 *pcs)
927{
928 nlsTime(pcs,
929 pszBuf,
930 pfTime->hours,
931 pfTime->minutes,
932 pfTime->twosecs * 2);
933}
934
935/*
936 *@@ nlsDate:
937 *
938 *@@added V1.0.1 (2003-01-17) [umoeller]
939 */
940
941VOID nlsDate(const COUNTRYSETTINGS2 *pcs2,
942 PSZ pszDate, // out: date string returned
943 USHORT year,
944 BYTE month,
945 BYTE day)
946{
947 switch (pcs2->cs.ulDateFormat)
948 {
949 case 0: // mm.dd.yyyy (English)
950 sprintf(pszDate, "%02d%c%02d%c%04d",
951 month,
952 pcs2->cs.cDateSep,
953 day,
954 pcs2->cs.cDateSep,
955 year);
956 break;
957
958 case 1: // dd.mm.yyyy (e.g. German)
959 sprintf(pszDate, "%02d%c%02d%c%04d",
960 day,
961 pcs2->cs.cDateSep,
962 month,
963 pcs2->cs.cDateSep,
964 year);
965 break;
966
967 case 2: // yyyy.mm.dd (ISO and Japanese)
968 sprintf(pszDate, "%04d%c%02d%c%02d",
969 year,
970 pcs2->cs.cDateSep,
971 month,
972 pcs2->cs.cDateSep,
973 day);
974 break;
975
976 default: // yyyy.dd.mm
977 sprintf(pszDate, "%04d%c%02d%c%02d",
978 year,
979 pcs2->cs.cDateSep,
980 day,
981 pcs2->cs.cDateSep,
982 month);
983 break;
984 }
985}
986
987/*
988 *@@ nlsTime:
989 *
990 *@@added V1.0.1 (2003-01-17) [umoeller]
991 */
992
993VOID nlsTime(const COUNTRYSETTINGS2 *pcs2,
994 PSZ pszTime, // out: time string returned
995 BYTE hours,
996 BYTE minutes,
997 BYTE seconds)
998{
999 if (!pcs2->cs.ulTimeFormat)
1000 {
1001 // for 12-hour clock, we need additional INI data
1002 if (hours >= 12) // V0.9.16 (2001-12-05) [pr] if (hours > 12)
1003 {
1004 // yeah cool Paul, now we get 00:20 PM if it's 20 past noon
1005 // V0.9.18 (2002-02-13) [umoeller]
1006 ULONG ulHours;
1007 if (!(ulHours = hours % 12))
1008 ulHours = 12;
1009
1010 // >= 12h: PM.
1011 sprintf(pszTime, "%02d%c%02d%c%02d %s",
1012 // leave 12 == 12 (not 0)
1013 ulHours,
1014 pcs2->cs.cTimeSep,
1015 minutes,
1016 pcs2->cs.cTimeSep,
1017 seconds,
1018 pcs2->ampm.sz2359);
1019 }
1020 else
1021 {
1022 // < 12h: AM
1023 sprintf(pszTime, "%02d%c%02d%c%02d %s",
1024 hours,
1025 pcs2->cs.cTimeSep,
1026 minutes,
1027 pcs2->cs.cTimeSep,
1028 seconds,
1029 pcs2->ampm.sz1159);
1030 }
1031 }
1032 else
1033 // 24-hour clock
1034 sprintf(pszTime, "%02d%c%02d%c%02d",
1035 hours,
1036 pcs2->cs.cTimeSep,
1037 minutes,
1038 pcs2->cs.cTimeSep,
1039 seconds);
1040}
1041
1042/*
1043 *@@ nlsDateTime:
1044 * converts Control Program DATETIME info
1045 * into two strings. See nlsFileDate and nlsFileTime
1046 * for more detailed parameter descriptions.
1047 *
1048 * Use of this function is deprecated. Use the
1049 * speedier nlsDateTime2 instead.
1050 *
1051 *@@added V0.9.0 (99-11-07) [umoeller]
1052 *@@changed V0.9.16 (2001-12-05) [pr]: fixed AM/PM hour bug
1053 *@@changed V0.9.18 (2002-02-13) [umoeller]: fixed AM/PM hour bug fix
1054 *@@changed V1.0.1 (2003-01-17) [umoeller]: extracted nlsDate, nlsTime
1055 */
1056
1057VOID nlsDateTime(PSZ pszDate, // out: date string returned (can be NULL)
1058 PSZ pszTime, // out: time string returned (can be NULL)
1059 const DATETIME *pDateTime, // in: date/time information
1060 ULONG ulDateFormat, // in: date format (0-3); see nlsFileDate
1061 CHAR cDateSep, // in: date separator (e.g. '.')
1062 ULONG ulTimeFormat, // in: 24-hour time format (0 or 1); see nlsFileTime
1063 CHAR cTimeSep) // in: time separator (e.g. ':')
1064{
1065 COUNTRYSETTINGS2 cs2;
1066 if (pszDate)
1067 {
1068 cs2.cs.ulDateFormat = ulDateFormat;
1069 cs2.cs.cDateSep = cDateSep;
1070 nlsDate(&cs2,
1071 pszDate, // out: date string returned
1072 pDateTime->year,
1073 pDateTime->month,
1074 pDateTime->day);
1075 }
1076
1077 if (pszTime)
1078 {
1079 cs2.cs.ulTimeFormat = ulTimeFormat;
1080 cs2.cs.cTimeSep = cTimeSep;
1081 nlsGetAMPM(&cs2.ampm);
1082 nlsTime(&cs2,
1083 pszTime,
1084 pDateTime->hours,
1085 pDateTime->minutes,
1086 pDateTime->seconds);
1087 }
1088}
1089
1090/*
1091 * strhDateTime:
1092 * wrapper around nlsDateTime for those who used
1093 * the XFLDR.DLL export.
1094 */
1095
1096VOID APIENTRY strhDateTime(PSZ pszDate, // out: date string returned (can be NULL)
1097 PSZ pszTime, // out: time string returned (can be NULL)
1098 DATETIME *pDateTime, // in: date/time information
1099 ULONG ulDateFormat, // in: date format (0-3); see nlsFileDate
1100 CHAR cDateSep, // in: date separator (e.g. '.')
1101 ULONG ulTimeFormat, // in: 24-hour time format (0 or 1); see nlsFileTime
1102 CHAR cTimeSep) // in: time separator (e.g. ':')
1103{
1104 nlsDateTime(pszDate,
1105 pszTime,
1106 pDateTime,
1107 ulDateFormat,
1108 cDateSep,
1109 ulTimeFormat,
1110 cTimeSep);
1111}
1112
1113/*
1114 *@@ nlsDateTime2:
1115 * speedier version of nlsDateTime that caches
1116 * all information and needs _no_ Prf* call
1117 * any more.
1118 *
1119 *@@added V1.0.1 (2003-01-17) [umoeller]
1120 */
1121
1122VOID nlsDateTime2(PSZ pszDate,
1123 PSZ pszTime,
1124 const DATETIME *pDateTime,
1125 const COUNTRYSETTINGS2 *pcs2)
1126{
1127 if (pszDate)
1128 nlsDate(pcs2,
1129 pszDate,
1130 pDateTime->year,
1131 pDateTime->month,
1132 pDateTime->day);
1133
1134 if (pszTime)
1135 nlsTime(pcs2,
1136 pszTime,
1137 pDateTime->hours,
1138 pDateTime->minutes,
1139 pDateTime->seconds);
1140}
1141
1142CHAR G_szUpperMap[257];
1143BOOL G_fUpperMapInited = FALSE;
1144
1145/*
1146 *@@ InitUpperMap:
1147 * initializes the case map for nlsUpper.
1148 *
1149 *@@added V0.9.20 (2002-07-25) [umoeller]
1150 */
1151
1152STATIC VOID InitUpperMap(VOID)
1153{
1154 ULONG ul;
1155 COUNTRYCODE cc;
1156 BOOL fDBCS = nlsDBCS();
1157
1158 for (ul = 0;
1159 ul < 256;
1160 ++ul)
1161 {
1162 G_szUpperMap[ul] = (CHAR)ul;
1163
1164 if ( (fDBCS)
1165 && (G_afLeadByte[ul] != TYPE_SBCS)
1166 )
1167 G_szUpperMap[ul] = ' ';
1168 }
1169
1170 G_szUpperMap[256] = '\0';
1171
1172 cc.country = 0; // use system country code
1173 cc.codepage = 0; // use process default codepage
1174 DosMapCase(255,
1175 &cc,
1176 G_szUpperMap + 1);
1177
1178 G_fUpperMapInited = TRUE;
1179}
1180
1181/*
1182 *@@ nlsUpper:
1183 * quick hack for upper-casing a string.
1184 *
1185 * This now returns the length of the given string always
1186 * (V0.9.20).
1187 *
1188 * Remarks:
1189 *
1190 * -- On the first call, we build a case map table by
1191 * calling DosMapCase with the default system country
1192 * code and the process's codepage. Since DosMapCase
1193 * is terribly slow with all the 16-bit thunking
1194 * involved, we can then use our table without having
1195 * to use the API ever again.
1196 *
1197 * -- As a result, if the process codepage changes for
1198 * any reason, this function will not pick up the
1199 * change.
1200 *
1201 * -- This has provisions for DBCS, which hopefully
1202 * work.
1203 *
1204 *@@added V0.9.16 (2001-10-25) [umoeller]
1205 *@@changed V0.9.20 (2002-07-25) [umoeller]: speedup, changed prototype
1206 *@@changed XWP V1.0.8 (2008-05-25) [pr]: rewritten for correct DBCS operation @@fixes 1070
1207 */
1208
1209ULONG nlsUpper(PSZ psz) // in/out: string
1210{
1211 BOOL bDBCSType = TYPE_SBCS;
1212 ULONG ul = 0;
1213 PSZ p = psz;
1214
1215 if (!G_fUpperMapInited)
1216 InitUpperMap();
1217
1218 for (; p && *p; p++, ul++)
1219 {
1220 switch(bDBCSType)
1221 {
1222 case TYPE_SBCS:
1223 case TYPE_DBCS_2ND:
1224 bDBCSType = G_afLeadByte[*p];
1225 break;
1226
1227 case TYPE_DBCS_1ST :
1228 bDBCSType = TYPE_DBCS_2ND;
1229 break;
1230 }
1231
1232 if (bDBCSType == TYPE_SBCS)
1233 *p = G_szUpperMap[*p];
1234 }
1235
1236 return ul;
1237}
1238
Note: See TracBrowser for help on using the repository browser.