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

Last change on this file since 325 was 319, checked in by pr, 19 years ago

Move doshIsWarp4 to dosh2 and query Syslevel to determine W4/MCP difference

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 32.6 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-2006 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 *@@changed V1.0.5 (2006-05-29) [pr]: Read Country rather than Locale settings on Warp 4 FP13+ @@fixes 614
382 */
383
384VOID nlsGetAMPM(PCOUNTRYAMPM pampm)
385{
386 if (doshIsWarp4()==3) // V1.0.5 (2006-05-29) [pr]
387 {
388 UconvObject uconv_object;
389
390 if (UniCreateUconvObject((UniChar *)L"",
391 &uconv_object) == ULS_SUCCESS)
392 {
393 LocaleObject locale_object;
394
395 if (UniCreateLocaleObject(UNI_UCS_STRING_POINTER,
396 (UniChar *)L"",
397 &locale_object) == ULS_SUCCESS)
398 {
399 int i;
400 struct LOCALE_ITEMLIST
401 {
402 LocaleItem lclItem;
403 PVOID vTarget;
404 int iType;
405 } itemList[] = {
406 { LOCI_s2359, &pampm->sz2359, 3 },
407 { LOCI_s1159, &pampm->sz1159, 3 }
408 };
409
410 for (i = 0;
411 i < sizeof(itemList) / sizeof(itemList[0]);
412 i++)
413 {
414 UniChar *pItem;
415
416 if (UniQueryLocaleItem(locale_object,
417 itemList[i].lclItem,
418 &pItem) == ULS_SUCCESS)
419 {
420 int iLen = UniStrlen(pItem) + 1;
421 PSZ pszResult = malloc(iLen);
422
423 if (UniStrFromUcs(uconv_object,
424 pszResult,
425 pItem,
426 iLen) == ULS_SUCCESS)
427 {
428 switch(itemList[i].iType)
429 {
430 case 1:
431 *((ULONG *) itemList[i].vTarget) = atol(pszResult);
432 break;
433
434 case 2:
435 *((CHAR *) itemList[i].vTarget) = pszResult[0];
436 break;
437
438 case 3:
439 strcpy (itemList[i].vTarget, pszResult);
440 break;
441 }
442 }
443
444 free(pszResult);
445 UniFreeMem(pItem);
446 }
447 }
448
449 UniFreeLocaleObject(locale_object);
450 }
451
452 UniFreeUconvObject(uconv_object);
453 }
454 }
455 else
456 {
457 PrfQueryProfileString(HINI_USER,
458 "PM_National",
459 "s2359", // key
460 "PM", // default
461 pampm->sz2359,
462 sizeof(pampm->sz2359));
463
464 PrfQueryProfileString(HINI_USER,
465 "PM_National",
466 "s1159", // key
467 "AM", // default
468 pampm->sz1159,
469 sizeof(pampm->sz1159));
470 }
471}
472
473/*
474 *@@ nlsQueryCountrySettings:
475 * this returns the most frequently used country settings
476 * all at once into a COUNTRYSETTINGS structure (prfh.h).
477 * This data corresponds to the user settings in the
478 * WPS "Country" object (which writes the data in "PM_National"
479 * in OS2.INI).
480 *
481 * In case a key cannot be found, the following (English)
482 * default values are set:
483 * -- ulDateFormat = 0 (English date format, mm.dd.yyyy);
484 * -- ulTimeFormat = 0 (12-hour clock);
485 * -- cDateSep = '/' (date separator);
486 * -- cTimeSep = ':' (time separator);
487 * -- cDecimal = '.' (decimal separator).
488 * -- cThousands = ',' (thousands separator).
489 *
490 *@@added V0.9.0 [umoeller]
491 *@@changed V0.9.7 (2000-12-02) [umoeller]: added cDecimal
492 *@@changed V1.0.4 (2005-10-15) [bvl]: Added support for Locale object settings on MCP systems @@fixes 614
493 *@@changed V1.0.4 (2005-10-29) [pr]: Rewritten to prevent memory leaks and errors
494 *@@changed V1.0.5 (2006-05-29) [pr]: Read Country rather than Locale settings on Warp 4 FP13+ @@fixes 614
495 */
496
497VOID nlsQueryCountrySettings(PCOUNTRYSETTINGS2 pcs2)
498{
499 if (pcs2)
500 {
501 PCOUNTRYSETTINGS pcs = &pcs2->cs;
502
503 if (doshIsWarp4()==3) // V1.0.5 (2006-05-29) [pr]
504 {
505 UconvObject uconv_object;
506
507 if (UniCreateUconvObject((UniChar *)L"",
508 &uconv_object) == ULS_SUCCESS)
509 {
510 LocaleObject locale_object;
511
512 if (UniCreateLocaleObject(UNI_UCS_STRING_POINTER,
513 (UniChar *)L"",
514 &locale_object) == ULS_SUCCESS)
515 {
516 int i;
517 struct LOCALE_ITEMLIST
518 {
519 LocaleItem lclItem;
520 PVOID vTarget;
521 int iType;
522 } itemList[] = {
523 { LOCI_iDate, &pcs->ulDateFormat, 1 },
524 { LOCI_iTime, &pcs->ulTimeFormat, 1 },
525 { LOCI_sDate, &pcs->cDateSep, 2 },
526 { LOCI_sTime, &pcs->cTimeSep, 2 },
527 { LOCI_sDecimal, &pcs->cDecimal, 2 },
528 { LOCI_sThousand, &pcs->cThousands, 2 }
529 };
530
531 for (i = 0;
532 i < sizeof(itemList) / sizeof(itemList[0]);
533 i++)
534 {
535 UniChar *pItem;
536
537 if (UniQueryLocaleItem(locale_object,
538 itemList[i].lclItem,
539 &pItem) == ULS_SUCCESS)
540 {
541 int iLen = UniStrlen(pItem) + 1;
542 PSZ pszResult = malloc(iLen);
543
544 if (UniStrFromUcs(uconv_object,
545 pszResult,
546 pItem,
547 iLen) == ULS_SUCCESS)
548 {
549 switch(itemList[i].iType)
550 {
551 case 1:
552 *((ULONG *) itemList[i].vTarget) = atol(pszResult);
553 break;
554
555 case 2:
556 *((CHAR *) itemList[i].vTarget) = pszResult[0];
557 break;
558
559 case 3:
560 strcpy (itemList[i].vTarget, pszResult);
561 break;
562 }
563 }
564
565 free(pszResult);
566 UniFreeMem(pItem);
567 }
568 }
569
570 UniFreeLocaleObject(locale_object);
571 }
572
573 UniFreeUconvObject(uconv_object);
574 }
575 }
576 else
577 {
578 pcs->ulDateFormat = PrfQueryProfileInt(HINI_USER,
579 (PSZ)PMINIAPP_NATIONAL,
580 "iDate",
581 0);
582 pcs->ulTimeFormat = PrfQueryProfileInt(HINI_USER,
583 (PSZ)PMINIAPP_NATIONAL,
584 "iTime",
585 0);
586 pcs->cDateSep = prfhQueryProfileChar(HINI_USER,
587 (PSZ)PMINIAPP_NATIONAL,
588 "sDate",
589 '/');
590 pcs->cTimeSep = prfhQueryProfileChar(HINI_USER,
591 (PSZ)PMINIAPP_NATIONAL,
592 "sTime",
593 ':');
594 pcs->cDecimal = prfhQueryProfileChar(HINI_USER,
595 (PSZ)PMINIAPP_NATIONAL,
596 "sDecimal",
597 '.');
598 pcs->cThousands = prfhQueryProfileChar(HINI_USER,
599 (PSZ)PMINIAPP_NATIONAL,
600 "sThousand",
601 ',');
602 }
603
604 nlsGetAMPM(&pcs2->ampm);
605 }
606}
607
608/*
609 *@@ nlsThousandsULong:
610 * converts a ULONG into a decimal string, while
611 * inserting thousands separators into it. Specify
612 * the separator character in cThousands.
613 *
614 * Returns pszTarget so you can use it directly
615 * with sprintf and the "%s" flag.
616 *
617 * For cThousands, you should use the data in
618 * OS2.INI ("PM_National" application), which is
619 * always set according to the "Country" object.
620 * You can use nlsQueryCountrySettings to
621 * retrieve this setting.
622 *
623 * Use nlsThousandsDouble for "double" values.
624 *
625 *@@changed V0.9.20 (2002-07-03) [umoeller]: optimized
626 */
627
628PSZ nlsThousandsULong(PSZ pszTarget, // out: decimal as string
629 ULONG ul, // in: decimal to convert
630 CHAR cThousands) // in: separator char (e.g. '.')
631{
632 CHAR szTemp[30];
633 USHORT ust = 0,
634 uss,
635 usLen = sprintf(szTemp, "%lu", ul); // V0.9.20 (2002-07-03) [umoeller]
636
637 for (uss = 0;
638 uss < usLen;
639 ++uss)
640 {
641 if (uss)
642 if (0 == ((usLen - uss) % 3))
643 {
644 pszTarget[ust] = cThousands;
645 ust++;
646 }
647
648 pszTarget[ust++] = szTemp[uss];
649 }
650
651 pszTarget[ust] = '\0';
652
653 return pszTarget;
654}
655
656/*
657 * strhThousandsULong:
658 * wrapper around nlsThousandsULong for those
659 * who used the XFLDR.DLL export.
660 *
661 *added V0.9.16 (2001-10-11) [umoeller]
662 */
663
664PSZ APIENTRY strhThousandsULong(PSZ pszTarget, // out: decimal as string
665 ULONG ul, // in: decimal to convert
666 CHAR cThousands) // in: separator char (e.g. '.')
667{
668 return nlsThousandsULong(pszTarget, ul, cThousands);
669}
670
671/*
672 *@@ nlsThousandsDouble:
673 * like nlsThousandsULong, but for a "double"
674 * value. Note that after-comma values are truncated.
675 *
676 *@@changed V0.9.20 (2002-07-03) [umoeller]: optimized
677 */
678
679PSZ nlsThousandsDouble(PSZ pszTarget,
680 double dbl,
681 CHAR cThousands)
682{
683 USHORT ust, uss, usLen;
684 CHAR szTemp[40];
685 usLen = sprintf(szTemp, "%.0f", floor(dbl)); // V0.9.20 (2002-07-03) [umoeller]
686
687 ust = 0;
688 for (uss = 0; uss < usLen; uss++)
689 {
690 if (uss)
691 if (((usLen - uss) % 3) == 0)
692 {
693 pszTarget[ust] = cThousands;
694 ust++;
695 }
696 pszTarget[ust] = szTemp[uss];
697 ust++;
698 }
699 pszTarget[ust] = '\0';
700
701 return pszTarget;
702}
703
704/*
705 *@@ nlsVariableDouble:
706 * like nlsThousandsULong, but for a "double" value, and
707 * with a variable number of decimal places depending on the
708 * size of the quantity.
709 *
710 *@@added V0.9.6 (2000-11-12) [pr]
711 *@@changed V0.9.20 (2002-07-03) [umoeller]: now using PCSZ pcszUnits
712 */
713
714PSZ nlsVariableDouble(PSZ pszTarget,
715 double dbl,
716 PCSZ pcszUnits,
717 CHAR cThousands)
718{
719 if (dbl < 100.0)
720 sprintf(pszTarget, "%.2f%s", dbl, pcszUnits);
721 else
722 if (dbl < 1000.0)
723 sprintf(pszTarget, "%.1f%s", dbl, pcszUnits);
724 else
725 strcat(nlsThousandsDouble(pszTarget, dbl, cThousands),
726 pcszUnits);
727
728 return pszTarget;
729}
730
731/*
732 *@@ nlsFileDate:
733 * converts file date data to a string (to pszBuf).
734 * You can pass any FDATE structure to this function,
735 * which are returned in those FILEFINDBUF* or
736 * FILESTATUS* structs by the Dos* functions.
737 *
738 * ulDateFormat is the PM setting for the date format,
739 * as set in the "Country" object, and can be queried using
740 + PrfQueryProfileInt(HINI_USER, "PM_National", "iDate", 0);
741 *
742 * meaning:
743 * -- 0 mm.dd.yyyy (English)
744 * -- 1 dd.mm.yyyy (e.g. German)
745 * -- 2 yyyy.mm.dd (Japanese, ISO)
746 * -- 3 yyyy.dd.mm
747 *
748 * cDateSep is used as a date separator (e.g. '.').
749 * This can be queried using:
750 + prfhQueryProfileChar(HINI_USER, "PM_National", "sDate", '/');
751 *
752 * Alternatively, you can query all the country settings
753 * at once using nlsQueryCountrySettings (prfh.c).
754 *
755 *@@changed V0.9.0 (99-11-07) [umoeller]: now calling nlsDateTime
756 *@@changed V1.0.1 (2003-01-17) [umoeller]: prototype changed for optimization, this was not exported
757 */
758
759VOID nlsFileDate(PSZ pszBuf, // out: string returned
760 const FDATE *pfDate, // in: date information
761 const COUNTRYSETTINGS2 *pcs)
762{
763 nlsDate(pcs,
764 pszBuf,
765 pfDate->year + 1980,
766 pfDate->month,
767 pfDate->day);
768}
769
770/*
771 *@@ nlsFileTime:
772 * converts file time data to a string (to pszBuf).
773 * You can pass any FTIME structure to this function,
774 * which are returned in those FILEFINDBUF* or
775 * FILESTATUS* structs by the Dos* functions.
776 *
777 * ulTimeFormat is the PM setting for the time format,
778 * as set in the "Country" object, and can be queried using
779 + PrfQueryProfileInt(HINI_USER, "PM_National", "iTime", 0);
780 * meaning:
781 * -- 0 12-hour clock
782 * -- >0 24-hour clock
783 *
784 * cDateSep is used as a time separator (e.g. ':').
785 * This can be queried using:
786 + prfhQueryProfileChar(HINI_USER, "PM_National", "sTime", ':');
787 *
788 * Alternatively, you can query all the country settings
789 * at once using nlsQueryCountrySettings (prfh.c).
790 *
791 *@@changed V0.8.5 (99-03-15) [umoeller]: fixed 12-hour crash
792 *@@changed V0.9.0 (99-11-07) [umoeller]: now calling nlsDateTime
793 *@@changed V1.0.1 (2003-01-17) [umoeller]: prototype changed for optimization, this was not exported
794 */
795
796VOID nlsFileTime(PSZ pszBuf, // out: string returned
797 const FTIME *pfTime, // in: time information
798 const COUNTRYSETTINGS2 *pcs)
799{
800 nlsTime(pcs,
801 pszBuf,
802 pfTime->hours,
803 pfTime->minutes,
804 pfTime->twosecs * 2);
805}
806
807/*
808 *@@ nlsDate:
809 *
810 *@@added V1.0.1 (2003-01-17) [umoeller]
811 */
812
813VOID nlsDate(const COUNTRYSETTINGS2 *pcs2,
814 PSZ pszDate, // out: date string returned
815 USHORT year,
816 BYTE month,
817 BYTE day)
818{
819 switch (pcs2->cs.ulDateFormat)
820 {
821 case 0: // mm.dd.yyyy (English)
822 sprintf(pszDate, "%02d%c%02d%c%04d",
823 month,
824 pcs2->cs.cDateSep,
825 day,
826 pcs2->cs.cDateSep,
827 year);
828 break;
829
830 case 1: // dd.mm.yyyy (e.g. German)
831 sprintf(pszDate, "%02d%c%02d%c%04d",
832 day,
833 pcs2->cs.cDateSep,
834 month,
835 pcs2->cs.cDateSep,
836 year);
837 break;
838
839 case 2: // yyyy.mm.dd (ISO and Japanese)
840 sprintf(pszDate, "%04d%c%02d%c%02d",
841 year,
842 pcs2->cs.cDateSep,
843 month,
844 pcs2->cs.cDateSep,
845 day);
846 break;
847
848 default: // yyyy.dd.mm
849 sprintf(pszDate, "%04d%c%02d%c%02d",
850 year,
851 pcs2->cs.cDateSep,
852 day,
853 pcs2->cs.cDateSep,
854 month);
855 break;
856 }
857}
858
859/*
860 *@@ nlsTime:
861 *
862 *@@added V1.0.1 (2003-01-17) [umoeller]
863 */
864
865VOID nlsTime(const COUNTRYSETTINGS2 *pcs2,
866 PSZ pszTime, // out: time string returned
867 BYTE hours,
868 BYTE minutes,
869 BYTE seconds)
870{
871 if (!pcs2->cs.ulTimeFormat)
872 {
873 // for 12-hour clock, we need additional INI data
874 if (hours >= 12) // V0.9.16 (2001-12-05) [pr] if (hours > 12)
875 {
876 // yeah cool Paul, now we get 00:20 PM if it's 20 past noon
877 // V0.9.18 (2002-02-13) [umoeller]
878 ULONG ulHours;
879 if (!(ulHours = hours % 12))
880 ulHours = 12;
881
882 // >= 12h: PM.
883 sprintf(pszTime, "%02d%c%02d%c%02d %s",
884 // leave 12 == 12 (not 0)
885 ulHours,
886 pcs2->cs.cTimeSep,
887 minutes,
888 pcs2->cs.cTimeSep,
889 seconds,
890 pcs2->ampm.sz2359);
891 }
892 else
893 {
894 // < 12h: AM
895 sprintf(pszTime, "%02d%c%02d%c%02d %s",
896 hours,
897 pcs2->cs.cTimeSep,
898 minutes,
899 pcs2->cs.cTimeSep,
900 seconds,
901 pcs2->ampm.sz1159);
902 }
903 }
904 else
905 // 24-hour clock
906 sprintf(pszTime, "%02d%c%02d%c%02d",
907 hours,
908 pcs2->cs.cTimeSep,
909 minutes,
910 pcs2->cs.cTimeSep,
911 seconds);
912}
913
914/*
915 *@@ nlsDateTime:
916 * converts Control Program DATETIME info
917 * into two strings. See nlsFileDate and nlsFileTime
918 * for more detailed parameter descriptions.
919 *
920 * Use of this function is deprecated. Use the
921 * speedier nlsDateTime2 instead.
922 *
923 *@@added V0.9.0 (99-11-07) [umoeller]
924 *@@changed V0.9.16 (2001-12-05) [pr]: fixed AM/PM hour bug
925 *@@changed V0.9.18 (2002-02-13) [umoeller]: fixed AM/PM hour bug fix
926 *@@changed V1.0.1 (2003-01-17) [umoeller]: extracted nlsDate, nlsTime
927 */
928
929VOID nlsDateTime(PSZ pszDate, // out: date string returned (can be NULL)
930 PSZ pszTime, // out: time string returned (can be NULL)
931 const DATETIME *pDateTime, // in: date/time information
932 ULONG ulDateFormat, // in: date format (0-3); see nlsFileDate
933 CHAR cDateSep, // in: date separator (e.g. '.')
934 ULONG ulTimeFormat, // in: 24-hour time format (0 or 1); see nlsFileTime
935 CHAR cTimeSep) // in: time separator (e.g. ':')
936{
937 COUNTRYSETTINGS2 cs2;
938 if (pszDate)
939 {
940 cs2.cs.ulDateFormat = ulDateFormat;
941 cs2.cs.cDateSep = cDateSep;
942 nlsDate(&cs2,
943 pszDate, // out: date string returned
944 pDateTime->year,
945 pDateTime->month,
946 pDateTime->day);
947 }
948
949 if (pszTime)
950 {
951 cs2.cs.ulTimeFormat = ulTimeFormat;
952 cs2.cs.cTimeSep = cTimeSep;
953 nlsGetAMPM(&cs2.ampm);
954 nlsTime(&cs2,
955 pszTime,
956 pDateTime->hours,
957 pDateTime->minutes,
958 pDateTime->seconds);
959 }
960}
961
962/*
963 * strhDateTime:
964 * wrapper around nlsDateTime for those who used
965 * the XFLDR.DLL export.
966 */
967
968VOID APIENTRY strhDateTime(PSZ pszDate, // out: date string returned (can be NULL)
969 PSZ pszTime, // out: time string returned (can be NULL)
970 DATETIME *pDateTime, // in: date/time information
971 ULONG ulDateFormat, // in: date format (0-3); see nlsFileDate
972 CHAR cDateSep, // in: date separator (e.g. '.')
973 ULONG ulTimeFormat, // in: 24-hour time format (0 or 1); see nlsFileTime
974 CHAR cTimeSep) // in: time separator (e.g. ':')
975{
976 nlsDateTime(pszDate,
977 pszTime,
978 pDateTime,
979 ulDateFormat,
980 cDateSep,
981 ulTimeFormat,
982 cTimeSep);
983}
984
985/*
986 *@@ nlsDateTime2:
987 * speedier version of nlsDateTime that caches
988 * all information and needs _no_ Prf* call
989 * any more.
990 *
991 *@@added V1.0.1 (2003-01-17) [umoeller]
992 */
993
994VOID nlsDateTime2(PSZ pszDate,
995 PSZ pszTime,
996 const DATETIME *pDateTime,
997 const COUNTRYSETTINGS2 *pcs2)
998{
999 if (pszDate)
1000 nlsDate(pcs2,
1001 pszDate,
1002 pDateTime->year,
1003 pDateTime->month,
1004 pDateTime->day);
1005
1006 if (pszTime)
1007 nlsTime(pcs2,
1008 pszTime,
1009 pDateTime->hours,
1010 pDateTime->minutes,
1011 pDateTime->seconds);
1012}
1013
1014CHAR G_szUpperMap[257];
1015BOOL G_fUpperMapInited = FALSE;
1016
1017/*
1018 *@@ InitUpperMap:
1019 * initializes the case map for nlsUpper.
1020 *
1021 *@@added V0.9.20 (2002-07-25) [umoeller]
1022 */
1023
1024STATIC VOID InitUpperMap(VOID)
1025{
1026 ULONG ul;
1027 COUNTRYCODE cc;
1028 BOOL fDBCS = nlsDBCS();
1029
1030 for (ul = 0;
1031 ul < 256;
1032 ++ul)
1033 {
1034 G_szUpperMap[ul] = (CHAR)ul;
1035
1036 if ( (fDBCS)
1037 && (G_afLeadByte[ul] != TYPE_SBCS)
1038 )
1039 G_szUpperMap[ul] = ' ';
1040 }
1041
1042 G_szUpperMap[256] = '\0';
1043
1044 cc.country = 0; // use system country code
1045 cc.codepage = 0; // use process default codepage
1046 DosMapCase(255,
1047 &cc,
1048 G_szUpperMap + 1);
1049
1050 G_fUpperMapInited = TRUE;
1051}
1052
1053/*
1054 *@@ nlsUpper:
1055 * quick hack for upper-casing a string.
1056 *
1057 * This now returns the length of the given string always
1058 * (V0.9.20).
1059 *
1060 * Remarks:
1061 *
1062 * -- On the first call, we build a case map table by
1063 * calling DosMapCase with the default system country
1064 * code and the process's codepage. Since DosMapCase
1065 * is terribly slow with all the 16-bit thunking
1066 * involved, we can then use our table without having
1067 * to use the API ever again.
1068 *
1069 * -- As a result, if the process codepage changes for
1070 * any reason, this function will not pick up the
1071 * change.
1072 *
1073 * -- This has provisions for DBCS, which hopefully
1074 * work.
1075 *
1076 *@@added V0.9.16 (2001-10-25) [umoeller]
1077 *@@changed V0.9.20 (2002-07-25) [umoeller]: speedup, changed prototype
1078 */
1079
1080ULONG nlsUpper(PSZ psz) // in/out: string
1081{
1082 ULONG ul = 0;
1083 PSZ p;
1084
1085 if (!G_fUpperMapInited)
1086 InitUpperMap();
1087
1088 if (p = psz)
1089 {
1090 while (*p++ = G_szUpperMap[*p])
1091 ++ul;
1092 }
1093
1094 return ul;
1095}
1096
1097
Note: See TracBrowser for help on using the repository browser.