source: trunk/src/helpers/nls.c

Last change on this file was 439, checked in by rlwalsh, 7 years ago

nls: fix DBCS handling in nlschr()
patch from Steve Levine

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