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

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