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

Last change on this file since 310 was 304, checked in by pr, 20 years ago

Bug 614 fixes for NLS on MCP systems

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