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

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

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

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