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

Last change on this file since 311 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: 27.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-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 */
378
379VOID nlsQueryCountrySettings(PCOUNTRYSETTINGS pcs)
380{
381 if (pcs)
382 {
383 pcs->ulDateFormat = PrfQueryProfileInt(HINI_USER,
384 (PSZ)PMINIAPP_NATIONAL,
385 "iDate",
386 0);
387 pcs->ulTimeFormat = PrfQueryProfileInt(HINI_USER,
388 (PSZ)PMINIAPP_NATIONAL,
389 "iTime",
390 0);
391 pcs->cDateSep = prfhQueryProfileChar(HINI_USER,
392 (PSZ)PMINIAPP_NATIONAL,
393 "sDate",
394 '/');
395 pcs->cTimeSep = prfhQueryProfileChar(HINI_USER,
396 (PSZ)PMINIAPP_NATIONAL,
397 "sTime",
398 ':');
399 pcs->cDecimal = prfhQueryProfileChar(HINI_USER,
400 (PSZ)PMINIAPP_NATIONAL,
401 "sDecimal",
402 '.');
403 pcs->cThousands = prfhQueryProfileChar(HINI_USER,
404 (PSZ)PMINIAPP_NATIONAL,
405 "sThousand",
406 ',');
407 if (doshIsWarp4()==2)
408 {
409 UconvObject uconv_object;
410
411 if (UniCreateUconvObject((UniChar *)L"",
412 &uconv_object) == ULS_SUCCESS)
413 {
414 LocaleObject locale_object;
415
416 if (UniCreateLocaleObject(UNI_UCS_STRING_POINTER,
417 (UniChar *)L"",
418 &locale_object) == ULS_SUCCESS)
419 {
420 int i;
421 struct LOCALE_ITEMLIST
422 {
423 LocaleItem lclItem;
424 PVOID vTarget;
425 int iType;
426 } itemList[] = {
427 { LOCI_iDate, &pcs->ulDateFormat, 1 },
428 { LOCI_iTime, &pcs->ulTimeFormat, 1 },
429 { LOCI_sDate, &pcs->cDateSep, 2 },
430 { LOCI_sTime, &pcs->cTimeSep, 2 },
431 { LOCI_sDecimal, &pcs->cDecimal, 2 },
432 { LOCI_sThousand, &pcs->cThousands, 2 }
433 };
434
435 for (i = 0;
436 i < sizeof(itemList) / sizeof(itemList[0]);
437 i++)
438 {
439 UniChar *pItem;
440
441 if (UniQueryLocaleItem(locale_object,
442 itemList[i].lclItem,
443 &pItem) == ULS_SUCCESS)
444 {
445 int iLen = UniStrlen(pItem) + 1;
446 PSZ pszResult = malloc(iLen);
447
448 if (UniStrFromUcs(uconv_object,
449 pszResult,
450 pItem,
451 iLen) == ULS_SUCCESS)
452 {
453 switch(itemList[i].iType)
454 {
455 case 1:
456 *((ULONG *) itemList[i].vTarget) = atol(pszResult);
457 break;
458
459 case 2:
460 *((CHAR *) itemList[i].vTarget) = pszResult[0];
461 break;
462 }
463 }
464
465 free(pszResult);
466 UniFreeMem(pItem);
467 }
468 }
469
470 UniFreeLocaleObject(locale_object);
471 }
472
473 UniFreeUconvObject(uconv_object);
474 }
475 }
476 }
477}
478
479/*
480 *@@ nlsThousandsULong:
481 * converts a ULONG into a decimal string, while
482 * inserting thousands separators into it. Specify
483 * the separator character in cThousands.
484 *
485 * Returns pszTarget so you can use it directly
486 * with sprintf and the "%s" flag.
487 *
488 * For cThousands, you should use the data in
489 * OS2.INI ("PM_National" application), which is
490 * always set according to the "Country" object.
491 * You can use nlsQueryCountrySettings to
492 * retrieve this setting.
493 *
494 * Use nlsThousandsDouble for "double" values.
495 *
496 *@@changed V0.9.20 (2002-07-03) [umoeller]: optimized
497 */
498
499PSZ nlsThousandsULong(PSZ pszTarget, // out: decimal as string
500 ULONG ul, // in: decimal to convert
501 CHAR cThousands) // in: separator char (e.g. '.')
502{
503 USHORT ust, uss, usc;
504 CHAR szTemp[40];
505 usc = sprintf(szTemp, "%lu", ul); // V0.9.20 (2002-07-03) [umoeller]
506
507 ust = 0;
508 // usc = strlen(szTemp);
509 for (uss = 0; uss < usc; uss++)
510 {
511 if (uss)
512 if (((usc - uss) % 3) == 0)
513 {
514 pszTarget[ust] = cThousands;
515 ust++;
516 }
517 pszTarget[ust] = szTemp[uss];
518 ust++;
519 }
520 pszTarget[ust] = '\0';
521
522 return pszTarget;
523}
524
525/*
526 * strhThousandsULong:
527 * wrapper around nlsThousandsULong for those
528 * who used the XFLDR.DLL export.
529 *
530 *added V0.9.16 (2001-10-11) [umoeller]
531 */
532
533PSZ APIENTRY strhThousandsULong(PSZ pszTarget, // out: decimal as string
534 ULONG ul, // in: decimal to convert
535 CHAR cThousands) // in: separator char (e.g. '.')
536{
537 return nlsThousandsULong(pszTarget, ul, cThousands);
538}
539
540/*
541 *@@ nlsThousandsDouble:
542 * like nlsThousandsULong, but for a "double"
543 * value. Note that after-comma values are truncated.
544 *
545 *@@changed V0.9.20 (2002-07-03) [umoeller]: optimized
546 */
547
548PSZ nlsThousandsDouble(PSZ pszTarget,
549 double dbl,
550 CHAR cThousands)
551{
552 USHORT ust, uss, usc;
553 CHAR szTemp[40];
554 usc = sprintf(szTemp, "%.0f", floor(dbl)); // V0.9.20 (2002-07-03) [umoeller]
555
556 ust = 0;
557 // usc = strlen(szTemp);
558 for (uss = 0; uss < usc; uss++)
559 {
560 if (uss)
561 if (((usc - uss) % 3) == 0)
562 {
563 pszTarget[ust] = cThousands;
564 ust++;
565 }
566 pszTarget[ust] = szTemp[uss];
567 ust++;
568 }
569 pszTarget[ust] = '\0';
570
571 return pszTarget;
572}
573
574/*
575 *@@ nlsVariableDouble:
576 * like nlsThousandsULong, but for a "double" value, and
577 * with a variable number of decimal places depending on the
578 * size of the quantity.
579 *
580 *@@added V0.9.6 (2000-11-12) [pr]
581 *@@changed V0.9.20 (2002-07-03) [umoeller]: now using PCSZ pcszUnits
582 */
583
584PSZ nlsVariableDouble(PSZ pszTarget,
585 double dbl,
586 PCSZ pcszUnits,
587 CHAR cThousands)
588{
589 if (dbl < 100.0)
590 sprintf(pszTarget, "%.2f%s", dbl, pcszUnits);
591 else
592 if (dbl < 1000.0)
593 sprintf(pszTarget, "%.1f%s", dbl, pcszUnits);
594 else
595 strcat(nlsThousandsDouble(pszTarget, dbl, cThousands),
596 pcszUnits);
597
598 return pszTarget;
599}
600
601/*
602 *@@ nlsFileDate:
603 * converts file date data to a string (to pszBuf).
604 * You can pass any FDATE structure to this function,
605 * which are returned in those FILEFINDBUF* or
606 * FILESTATUS* structs by the Dos* functions.
607 *
608 * ulDateFormat is the PM setting for the date format,
609 * as set in the "Country" object, and can be queried using
610 + PrfQueryProfileInt(HINI_USER, "PM_National", "iDate", 0);
611 *
612 * meaning:
613 * -- 0 mm.dd.yyyy (English)
614 * -- 1 dd.mm.yyyy (e.g. German)
615 * -- 2 yyyy.mm.dd (Japanese, ISO)
616 * -- 3 yyyy.dd.mm
617 *
618 * cDateSep is used as a date separator (e.g. '.').
619 * This can be queried using:
620 + prfhQueryProfileChar(HINI_USER, "PM_National", "sDate", '/');
621 *
622 * Alternatively, you can query all the country settings
623 * at once using nlsQueryCountrySettings (prfh.c).
624 *
625 *@@changed V0.9.0 (99-11-07) [umoeller]: now calling nlsDateTime
626 */
627
628VOID nlsFileDate(PSZ pszBuf, // out: string returned
629 FDATE *pfDate, // in: date information
630 ULONG ulDateFormat, // in: date format (0-3)
631 CHAR cDateSep) // in: date separator (e.g. '.')
632{
633 DATETIME dt;
634 dt.day = pfDate->day;
635 dt.month = pfDate->month;
636 dt.year = pfDate->year + 1980;
637
638 nlsDateTime(pszBuf,
639 NULL, // no time
640 &dt,
641 ulDateFormat,
642 cDateSep,
643 0, 0); // no time
644}
645
646/*
647 *@@ nlsFileTime:
648 * converts file time data to a string (to pszBuf).
649 * You can pass any FTIME structure to this function,
650 * which are returned in those FILEFINDBUF* or
651 * FILESTATUS* structs by the Dos* functions.
652 *
653 * ulTimeFormat is the PM setting for the time format,
654 * as set in the "Country" object, and can be queried using
655 + PrfQueryProfileInt(HINI_USER, "PM_National", "iTime", 0);
656 * meaning:
657 * -- 0 12-hour clock
658 * -- >0 24-hour clock
659 *
660 * cDateSep is used as a time separator (e.g. ':').
661 * This can be queried using:
662 + prfhQueryProfileChar(HINI_USER, "PM_National", "sTime", ':');
663 *
664 * Alternatively, you can query all the country settings
665 * at once using nlsQueryCountrySettings (prfh.c).
666 *
667 *@@changed V0.8.5 (99-03-15) [umoeller]: fixed 12-hour crash
668 *@@changed V0.9.0 (99-11-07) [umoeller]: now calling nlsDateTime
669 */
670
671VOID nlsFileTime(PSZ pszBuf, // out: string returned
672 FTIME *pfTime, // in: time information
673 ULONG ulTimeFormat, // in: 24-hour time format (0 or 1)
674 CHAR cTimeSep) // in: time separator (e.g. ':')
675{
676 DATETIME dt;
677 dt.hours = pfTime->hours;
678 dt.minutes = pfTime->minutes;
679 dt.seconds = pfTime->twosecs * 2;
680
681 nlsDateTime(NULL, // no date
682 pszBuf,
683 &dt,
684 0, 0, // no date
685 ulTimeFormat,
686 cTimeSep);
687}
688
689/*
690 *@@ nlsDateTime:
691 * converts Control Program DATETIME info
692 * into two strings. See nlsFileDate and nlsFileTime
693 * for more detailed parameter descriptions.
694 *
695 *@@added V0.9.0 (99-11-07) [umoeller]
696 *@@changed V0.9.16 (2001-12-05) [pr]: fixed AM/PM hour bug
697 *@@changed V0.9.18 (2002-02-13) [umoeller]: fixed AM/PM hour bug fix
698 */
699
700VOID nlsDateTime(PSZ pszDate, // out: date string returned (can be NULL)
701 PSZ pszTime, // out: time string returned (can be NULL)
702 DATETIME *pDateTime, // in: date/time information
703 ULONG ulDateFormat, // in: date format (0-3); see nlsFileDate
704 CHAR cDateSep, // in: date separator (e.g. '.')
705 ULONG ulTimeFormat, // in: 24-hour time format (0 or 1); see nlsFileTime
706 CHAR cTimeSep) // in: time separator (e.g. ':')
707{
708 if (pszDate)
709 {
710 switch (ulDateFormat)
711 {
712 case 0: // mm.dd.yyyy (English)
713 sprintf(pszDate, "%02d%c%02d%c%04d",
714 pDateTime->month,
715 cDateSep,
716 pDateTime->day,
717 cDateSep,
718 pDateTime->year);
719 break;
720
721 case 1: // dd.mm.yyyy (e.g. German)
722 sprintf(pszDate, "%02d%c%02d%c%04d",
723 pDateTime->day,
724 cDateSep,
725 pDateTime->month,
726 cDateSep,
727 pDateTime->year);
728 break;
729
730 case 2: // yyyy.mm.dd (Japanese)
731 sprintf(pszDate, "%04d%c%02d%c%02d",
732 pDateTime->year,
733 cDateSep,
734 pDateTime->month,
735 cDateSep,
736 pDateTime->day);
737 break;
738
739 default: // yyyy.dd.mm
740 sprintf(pszDate, "%04d%c%02d%c%02d",
741 pDateTime->year,
742 cDateSep,
743 pDateTime->day,
744 cDateSep,
745 pDateTime->month);
746 break;
747 }
748 }
749
750 if (pszTime)
751 {
752 if (ulTimeFormat == 0)
753 {
754 // for 12-hour clock, we need additional INI data
755 CHAR szAMPM[10] = "err";
756
757 if (pDateTime->hours >= 12) // V0.9.16 (2001-12-05) [pr] if (pDateTime->hours > 12)
758 {
759 // yeah cool Paul, now we get 00:20 PM if it's 20 past noon
760 // V0.9.18 (2002-02-13) [umoeller]
761 ULONG ulHours;
762 if (!(ulHours = pDateTime->hours % 12))
763 ulHours = 12;
764
765 // >= 12h: PM.
766 PrfQueryProfileString(HINI_USER,
767 "PM_National",
768 "s2359", // key
769 "PM", // default
770 szAMPM, sizeof(szAMPM)-1);
771 sprintf(pszTime, "%02d%c%02d%c%02d %s",
772 // leave 12 == 12 (not 0)
773 ulHours,
774 cTimeSep,
775 pDateTime->minutes,
776 cTimeSep,
777 pDateTime->seconds,
778 szAMPM);
779 }
780 else
781 {
782 // < 12h: AM
783 PrfQueryProfileString(HINI_USER,
784 "PM_National",
785 "s1159", // key
786 "AM", // default
787 szAMPM, sizeof(szAMPM)-1);
788 sprintf(pszTime, "%02d%c%02d%c%02d %s",
789 pDateTime->hours,
790 cTimeSep,
791 pDateTime->minutes,
792 cTimeSep,
793 pDateTime->seconds,
794 szAMPM);
795 }
796 }
797 else
798 // 24-hour clock
799 sprintf(pszTime, "%02d%c%02d%c%02d",
800 pDateTime->hours,
801 cTimeSep,
802 pDateTime->minutes,
803 cTimeSep,
804 pDateTime->seconds);
805 }
806}
807
808/*
809 * strhDateTime:
810 * wrapper around nlsDateTime for those who used
811 * the XFLDR.DLL export.
812 */
813
814VOID APIENTRY strhDateTime(PSZ pszDate, // out: date string returned (can be NULL)
815 PSZ pszTime, // out: time string returned (can be NULL)
816 DATETIME *pDateTime, // in: date/time information
817 ULONG ulDateFormat, // in: date format (0-3); see nlsFileDate
818 CHAR cDateSep, // in: date separator (e.g. '.')
819 ULONG ulTimeFormat, // in: 24-hour time format (0 or 1); see nlsFileTime
820 CHAR cTimeSep) // in: time separator (e.g. ':')
821{
822 nlsDateTime(pszDate,
823 pszTime,
824 pDateTime,
825 ulDateFormat,
826 cDateSep,
827 ulTimeFormat,
828 cTimeSep);
829}
830
831CHAR G_szUpperMap[257];
832BOOL G_fUpperMapInited = FALSE;
833
834/*
835 *@@ InitUpperMap:
836 * initializes the case map for nlsUpper.
837 *
838 *@@added V0.9.20 (2002-07-25) [umoeller]
839 */
840
841STATIC VOID InitUpperMap(VOID)
842{
843 ULONG ul;
844 COUNTRYCODE cc;
845 BOOL fDBCS = nlsDBCS();
846
847 for (ul = 0;
848 ul < sizeof(G_szUpperMap);
849 ++ul)
850 {
851 G_szUpperMap[ul] = (CHAR)ul;
852
853 if ( (fDBCS)
854 && (G_afLeadByte[ul] != TYPE_SBCS)
855 )
856 G_szUpperMap[ul] = ' ';
857 }
858
859 G_szUpperMap[256] = '\0';
860
861 cc.country = 0; // use system country code
862 cc.codepage = 0; // use process default codepage
863 DosMapCase(255,
864 &cc,
865 G_szUpperMap + 1);
866
867 G_fUpperMapInited = TRUE;
868}
869
870/*
871 *@@ nlsUpper:
872 * quick hack for upper-casing a string.
873 *
874 * This now returns the length of the given string always
875 * (V0.9.20).
876 *
877 * Remarks:
878 *
879 * -- On the first call, we build a case map table by
880 * calling DosMapCase with the default system country
881 * code and the process's codepage. Since DosMapCase
882 * is terribly slow with all the 16-bit thunking
883 * involved, we can then use our table without having
884 * to use the API ever again.
885 *
886 * -- As a result, if the process codepage changes for
887 * any reason, this function will not pick up the
888 * change.
889 *
890 * -- This has provisions for DBCS, which hopefully
891 * work.
892 *
893 *@@added V0.9.16 (2001-10-25) [umoeller]
894 *@@changed V0.9.20 (2002-07-25) [umoeller]: speedup, changed prototype
895 */
896
897ULONG nlsUpper(PSZ psz) // in/out: string
898{
899 ULONG ul = 0;
900
901 if (!G_fUpperMapInited)
902 InitUpperMap();
903
904 if (psz)
905 {
906 PSZ p = psz;
907
908 while (*p++ = G_szUpperMap[*p])
909 ++ul;
910 }
911
912 return ul;
913}
914
915
Note: See TracBrowser for help on using the repository browser.