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

Last change on this file was 444, checked in by rlwalsh, 6 years ago

nls: rewrite nlschr()

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 30.2 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 * This finds single-byte characters in strings
246 * that may contain DBCS characters. Both DBCS
247 * leading and trailing bytes are ignored, even
248 * if one of them matches the search character.
249 *
250 * If the system is not running with DBCS,
251 * this calls plain strchr automatically.
252 *
253 */
254
255PSZ nlschr(PCSZ p, char c)
256{
257 PCSZ p2;
258
259 // if not DBCS, do standard strchr()
260 if (!nlsDBCS())
261 return strchr(p, c);
262
263 // we can't find c if it is a DBCS lead byte
264 if (G_afLeadByte[c])
265 return NULL;
266
267 for (p2 = p; *p2; p2++)
268 {
269 // skip over DBCS leading bytes and their trailing bytes
270 if (G_afLeadByte[*p2]) {
271
272 // exit if the trailing byte is zero (shouldn't be)
273 if (!*(++p2))
274 return NULL;
275
276 continue;
277 }
278
279 // test the SBCS char at p2
280 if (*p2 == c)
281 return (PSZ)p2;
282 }
283
284 return NULL;
285}
286
287/*
288 *@@ nlsrchr:
289 * replacement for strrchr with DBCS support.
290 *
291 * If the system is not running with DBCS,
292 * this calls plain strrchr automatically.
293 *
294 *@@added V0.9.19 (2002-06-13) [umoeller]
295 *@@changed V0.9.20 (2002-07-22) [lafaix]: optimized
296 */
297
298PSZ nlsrchr(PCSZ p, char c)
299{
300 PCSZ p2,
301 pLast = NULL;
302 ULONG ulDBCSType = TYPE_SBCS;
303
304 if (!nlsDBCS())
305 // not DBCS:
306 return strrchr(p, c);
307
308 // we're on DBCS:
309
310 // we can't find c if it is a leading byte
311 if (G_afLeadByte[c] != TYPE_SBCS)
312 return NULL;
313
314 for (p2 = p;
315 *p2;
316 ++p2)
317 {
318 // check _previous_ DBCS type and refresh
319 // DBCS type accordingly
320 switch (ulDBCSType)
321 {
322 case TYPE_SBCS:
323 case TYPE_DBCS_2ND:
324 ulDBCSType = G_afLeadByte[*p2];
325 if (*p2 == c)
326 pLast = p2;
327 break;
328
329 case TYPE_DBCS_1ST :
330 ulDBCSType = TYPE_DBCS_2ND;
331 break;
332 }
333 }
334
335 return (PSZ)pLast;
336
337 // old code V0.9.20 (2002-07-22) [lafaix]
338 /*
339 ulLength = strlen(p);
340 for (p2 = p + ulLength - 1;
341 p2 >= p;
342 --p2)
343 {
344 if (*p2 == c)
345 {
346 // match: return this only if it's SBCS;
347 // if it's a DBCS trail char, skip it
348 switch (ulDBCS = nlsQueryDBCSChar(p, p2 - p))
349 {
350 case TYPE_SBCS:
351 return (PSZ)p2;
352
353 case TYPE_DBCS_2ND:
354 --p2;
355 }
356 }
357 }
358
359 return NULL;
360 */
361}
362
363/* ******************************************************************
364 *
365 * Country-dependent formatting
366 *
367 ********************************************************************/
368
369/*
370 *@@ nlsQueryCountrySettings:
371 * this returns the most frequently used country settings
372 * all at once into a COUNTRYSETTINGS structure (prfh.h).
373 * This data corresponds to the user settings in the
374 * WPS "Country" object (which writes the data in "PM_National"
375 * in OS2.INI).
376 *
377 * In case a key cannot be found, the following (English)
378 * default values are set:
379 * -- ulDateFormat = 0 (English date format, mm.dd.yyyy);
380 * -- ulTimeFormat = 0 (12-hour clock);
381 * -- cDateSep = '/' (date separator);
382 * -- cTimeSep = ':' (time separator);
383 * -- cDecimal = '.' (decimal separator).
384 * -- cThousands = ',' (thousands separator).
385 *
386 *@@added V0.9.0 [umoeller]
387 *@@changed V0.9.7 (2000-12-02) [umoeller]: added cDecimal
388 *@@changed V1.0.4 (2005-10-15) [bvl]: Added support for Locale object settings on MCP systems @@fixes 614
389 *@@changed V1.0.4 (2005-10-29) [pr]: Rewritten to prevent memory leaks and errors
390 *@@changed V1.0.5 (2006-05-29) [pr]: Read Country rather than Locale settings on Warp 4 FP13+ @@fixes 614
391 *@@changed WarpIN V1.0.15 (2007-03-26) [pr]: Rewritten to load UCONV/LIBUNI functions dynamically @@fixes 936
392 */
393
394VOID nlsQueryCountrySettings(PCOUNTRYSETTINGS pcs)
395{
396 if (pcs)
397 {
398 if ( (doshIsWarp4()==3)
399 && (!fUniResolved)
400 )
401 {
402 HMODULE hmodUCONV = NULLHANDLE,
403 hmodLIBUNI = NULLHANDLE;
404
405 fUniResolved = TRUE;
406 if ( (doshResolveImports("UCONV.DLL",
407 &hmodUCONV,
408 G_aResolveFromUCONV,
409 sizeof(G_aResolveFromUCONV) / sizeof(G_aResolveFromUCONV[0]))
410 == NO_ERROR)
411 && (doshResolveImports("LIBUNI.DLL",
412 &hmodLIBUNI,
413 G_aResolveFromLIBUNI,
414 sizeof(G_aResolveFromLIBUNI) / sizeof(G_aResolveFromLIBUNI[0]))
415 == NO_ERROR)
416 )
417 fUniOK = TRUE;
418 }
419
420 if ( (doshIsWarp4()==3) // V1.0.5 (2006-05-29) [pr]
421 && fUniOK
422 )
423 {
424 UconvObject uconv_object;
425
426 if (G_UniCreateUconvObject((UniChar *)L"",
427 &uconv_object) == ULS_SUCCESS)
428 {
429 LocaleObject locale_object;
430
431 if (G_UniCreateLocaleObject(UNI_UCS_STRING_POINTER,
432 (UniChar *)L"",
433 &locale_object) == ULS_SUCCESS)
434 {
435 int i;
436 struct LOCALE_ITEMLIST
437 {
438 LocaleItem lclItem;
439 PVOID vTarget;
440 int iType;
441 } itemList[] = {
442 { LOCI_iDate, &pcs->ulDateFormat, 1 },
443 { LOCI_iTime, &pcs->ulTimeFormat, 1 },
444 { LOCI_sDate, &pcs->cDateSep, 2 },
445 { LOCI_sTime, &pcs->cTimeSep, 2 },
446 { LOCI_sDecimal, &pcs->cDecimal, 2 },
447 { LOCI_sThousand, &pcs->cThousands, 2 }
448 };
449
450 for (i = 0;
451 i < sizeof(itemList) / sizeof(itemList[0]);
452 i++)
453 {
454 UniChar *pItem;
455
456 if (G_UniQueryLocaleItem(locale_object,
457 itemList[i].lclItem,
458 &pItem) == ULS_SUCCESS)
459 {
460 int iLen = G_UniStrlen(pItem) + 1;
461 PSZ pszResult = malloc(iLen);
462
463 if (G_UniStrFromUcs(uconv_object,
464 pszResult,
465 pItem,
466 iLen) == ULS_SUCCESS)
467 {
468 switch(itemList[i].iType)
469 {
470 case 1:
471 *((ULONG *) itemList[i].vTarget) = atol(pszResult);
472 break;
473
474 case 2:
475 *((CHAR *) itemList[i].vTarget) = pszResult[0];
476 break;
477 }
478 }
479
480 free(pszResult);
481 G_UniFreeMem(pItem);
482 }
483 }
484
485 G_UniFreeLocaleObject(locale_object);
486 }
487
488 G_UniFreeUconvObject(uconv_object);
489 }
490 }
491 else
492 {
493 pcs->ulDateFormat = PrfQueryProfileInt(HINI_USER,
494 (PSZ)PMINIAPP_NATIONAL,
495 "iDate",
496 0);
497 pcs->ulTimeFormat = PrfQueryProfileInt(HINI_USER,
498 (PSZ)PMINIAPP_NATIONAL,
499 "iTime",
500 0);
501 pcs->cDateSep = prfhQueryProfileChar(HINI_USER,
502 (PSZ)PMINIAPP_NATIONAL,
503 "sDate",
504 '/');
505 pcs->cTimeSep = prfhQueryProfileChar(HINI_USER,
506 (PSZ)PMINIAPP_NATIONAL,
507 "sTime",
508 ':');
509 pcs->cDecimal = prfhQueryProfileChar(HINI_USER,
510 (PSZ)PMINIAPP_NATIONAL,
511 "sDecimal",
512 '.');
513 pcs->cThousands = prfhQueryProfileChar(HINI_USER,
514 (PSZ)PMINIAPP_NATIONAL,
515 "sThousand",
516 ',');
517 }
518 }
519}
520
521/*
522 *@@ nlsThousandsULong:
523 * converts a ULONG into a decimal string, while
524 * inserting thousands separators into it. Specify
525 * the separator character in cThousands.
526 *
527 * Returns pszTarget so you can use it directly
528 * with sprintf and the "%s" flag.
529 *
530 * For cThousands, you should use the data in
531 * OS2.INI ("PM_National" application), which is
532 * always set according to the "Country" object.
533 * You can use nlsQueryCountrySettings to
534 * retrieve this setting.
535 *
536 * Use nlsThousandsDouble for "double" values.
537 *
538 *@@changed V0.9.20 (2002-07-03) [umoeller]: optimized
539 */
540
541PSZ nlsThousandsULong(PSZ pszTarget, // out: decimal as string
542 ULONG ul, // in: decimal to convert
543 CHAR cThousands) // in: separator char (e.g. '.')
544{
545 USHORT ust, uss, usc;
546 CHAR szTemp[40];
547 usc = sprintf(szTemp, "%lu", ul); // V0.9.20 (2002-07-03) [umoeller]
548
549 ust = 0;
550 // usc = strlen(szTemp);
551 for (uss = 0; uss < usc; uss++)
552 {
553 if (uss)
554 if (((usc - uss) % 3) == 0)
555 {
556 pszTarget[ust] = cThousands;
557 ust++;
558 }
559 pszTarget[ust++] = szTemp[uss];
560 }
561 pszTarget[ust] = '\0';
562
563 return pszTarget;
564}
565
566/*
567 * strhThousandsULong:
568 * wrapper around nlsThousandsULong for those
569 * who used the XFLDR.DLL export.
570 *
571 *added V0.9.16 (2001-10-11) [umoeller]
572 */
573
574PSZ APIENTRY strhThousandsULong(PSZ pszTarget, // out: decimal as string
575 ULONG ul, // in: decimal to convert
576 CHAR cThousands) // in: separator char (e.g. '.')
577{
578 return nlsThousandsULong(pszTarget, ul, cThousands);
579}
580
581/*
582 *@@ nlsThousandsDouble:
583 * like nlsThousandsULong, but for a "double"
584 * value. Note that after-comma values are truncated.
585 *
586 *@@changed V0.9.20 (2002-07-03) [umoeller]: optimized
587 *@@changed XWP V1.0.9 (2010-07-20) [pr]: added 0.5 to round
588 */
589
590PSZ nlsThousandsDouble(PSZ pszTarget,
591 double dbl,
592 CHAR cThousands)
593{
594 USHORT ust, uss, usc;
595 CHAR szTemp[40];
596 usc = sprintf(szTemp, "%.0f", floor(dbl + 0.5)); // V0.9.20 (2002-07-03) [umoeller]
597
598 ust = 0;
599 // usc = strlen(szTemp);
600 for (uss = 0; uss < usc; uss++)
601 {
602 if (uss)
603 if (((usc - uss) % 3) == 0)
604 {
605 pszTarget[ust] = cThousands;
606 ust++;
607 }
608 pszTarget[ust] = szTemp[uss];
609 ust++;
610 }
611 pszTarget[ust] = '\0';
612
613 return pszTarget;
614}
615
616/*
617 *@@ nlsVariableDouble:
618 * like nlsThousandsULong, but for a "double" value, and
619 * with a variable number of decimal places depending on the
620 * size of the quantity.
621 *
622 *@@added V0.9.6 (2000-11-12) [pr]
623 *@@changed V0.9.20 (2002-07-03) [umoeller]: now using PCSZ pcszUnits
624 */
625
626PSZ nlsVariableDouble(PSZ pszTarget,
627 double dbl,
628 PCSZ pcszUnits,
629 CHAR cThousands)
630{
631 if (dbl < 100.0)
632 sprintf(pszTarget, "%.2f%s", dbl, pcszUnits);
633 else
634 if (dbl < 1000.0)
635 sprintf(pszTarget, "%.1f%s", dbl, pcszUnits);
636 else
637 strcat(nlsThousandsDouble(pszTarget, dbl, cThousands),
638 pcszUnits);
639
640 return pszTarget;
641}
642
643/*
644 *@@ nlsFileDate:
645 * converts file date data to a string (to pszBuf).
646 * You can pass any FDATE structure to this function,
647 * which are returned in those FILEFINDBUF* or
648 * FILESTATUS* structs by the Dos* functions.
649 *
650 * ulDateFormat is the PM setting for the date format,
651 * as set in the "Country" object, and can be queried using
652 + PrfQueryProfileInt(HINI_USER, "PM_National", "iDate", 0);
653 *
654 * meaning:
655 * -- 0 mm.dd.yyyy (English)
656 * -- 1 dd.mm.yyyy (e.g. German)
657 * -- 2 yyyy.mm.dd (Japanese, ISO)
658 * -- 3 yyyy.dd.mm
659 *
660 * cDateSep is used as a date separator (e.g. '.').
661 * This can be queried using:
662 + prfhQueryProfileChar(HINI_USER, "PM_National", "sDate", '/');
663 *
664 * Alternatively, you can query all the country settings
665 * at once using nlsQueryCountrySettings (prfh.c).
666 *
667 *@@changed V0.9.0 (99-11-07) [umoeller]: now calling nlsDateTime
668 */
669
670VOID nlsFileDate(PSZ pszBuf, // out: string returned
671 FDATE *pfDate, // in: date information
672 ULONG ulDateFormat, // in: date format (0-3)
673 CHAR cDateSep) // in: date separator (e.g. '.')
674{
675 DATETIME dt;
676 dt.day = pfDate->day;
677 dt.month = pfDate->month;
678 dt.year = pfDate->year + 1980;
679
680 nlsDateTime(pszBuf,
681 NULL, // no time
682 &dt,
683 ulDateFormat,
684 cDateSep,
685 0, 0); // no time
686}
687
688/*
689 *@@ nlsFileTime:
690 * converts file time data to a string (to pszBuf).
691 * You can pass any FTIME structure to this function,
692 * which are returned in those FILEFINDBUF* or
693 * FILESTATUS* structs by the Dos* functions.
694 *
695 * ulTimeFormat is the PM setting for the time format,
696 * as set in the "Country" object, and can be queried using
697 + PrfQueryProfileInt(HINI_USER, "PM_National", "iTime", 0);
698 * meaning:
699 * -- 0 12-hour clock
700 * -- >0 24-hour clock
701 *
702 * cDateSep is used as a time separator (e.g. ':').
703 * This can be queried using:
704 + prfhQueryProfileChar(HINI_USER, "PM_National", "sTime", ':');
705 *
706 * Alternatively, you can query all the country settings
707 * at once using nlsQueryCountrySettings (prfh.c).
708 *
709 *@@changed V0.8.5 (99-03-15) [umoeller]: fixed 12-hour crash
710 *@@changed V0.9.0 (99-11-07) [umoeller]: now calling nlsDateTime
711 */
712
713VOID nlsFileTime(PSZ pszBuf, // out: string returned
714 FTIME *pfTime, // in: time information
715 ULONG ulTimeFormat, // in: 24-hour time format (0 or 1)
716 CHAR cTimeSep) // in: time separator (e.g. ':')
717{
718 DATETIME dt;
719 dt.hours = pfTime->hours;
720 dt.minutes = pfTime->minutes;
721 dt.seconds = pfTime->twosecs * 2;
722
723 nlsDateTime(NULL, // no date
724 pszBuf,
725 &dt,
726 0, 0, // no date
727 ulTimeFormat,
728 cTimeSep);
729}
730
731/*
732 *@@ nlsDateTime:
733 * converts Control Program DATETIME info
734 * into two strings. See nlsFileDate and nlsFileTime
735 * for more detailed parameter descriptions.
736 *
737 *@@added V0.9.0 (99-11-07) [umoeller]
738 *@@changed V0.9.16 (2001-12-05) [pr]: fixed AM/PM hour bug
739 *@@changed V0.9.18 (2002-02-13) [umoeller]: fixed AM/PM hour bug fix
740 */
741
742VOID nlsDateTime(PSZ pszDate, // out: date string returned (can be NULL)
743 PSZ pszTime, // out: time string returned (can be NULL)
744 DATETIME *pDateTime, // in: date/time information
745 ULONG ulDateFormat, // in: date format (0-3); see nlsFileDate
746 CHAR cDateSep, // in: date separator (e.g. '.')
747 ULONG ulTimeFormat, // in: 24-hour time format (0 or 1); see nlsFileTime
748 CHAR cTimeSep) // in: time separator (e.g. ':')
749{
750 if (pszDate)
751 {
752 switch (ulDateFormat)
753 {
754 case 0: // mm.dd.yyyy (English)
755 sprintf(pszDate, "%02d%c%02d%c%04d",
756 pDateTime->month,
757 cDateSep,
758 pDateTime->day,
759 cDateSep,
760 pDateTime->year);
761 break;
762
763 case 1: // dd.mm.yyyy (e.g. German)
764 sprintf(pszDate, "%02d%c%02d%c%04d",
765 pDateTime->day,
766 cDateSep,
767 pDateTime->month,
768 cDateSep,
769 pDateTime->year);
770 break;
771
772 case 2: // yyyy.mm.dd (Japanese)
773 sprintf(pszDate, "%04d%c%02d%c%02d",
774 pDateTime->year,
775 cDateSep,
776 pDateTime->month,
777 cDateSep,
778 pDateTime->day);
779 break;
780
781 default: // yyyy.dd.mm
782 sprintf(pszDate, "%04d%c%02d%c%02d",
783 pDateTime->year,
784 cDateSep,
785 pDateTime->day,
786 cDateSep,
787 pDateTime->month);
788 break;
789 }
790 }
791
792 if (pszTime)
793 {
794 if (ulTimeFormat == 0)
795 {
796 // for 12-hour clock, we need additional INI data
797 CHAR szAMPM[10] = "err";
798
799 if (pDateTime->hours >= 12) // V0.9.16 (2001-12-05) [pr] if (pDateTime->hours > 12)
800 {
801 // yeah cool Paul, now we get 00:20 PM if it's 20 past noon
802 // V0.9.18 (2002-02-13) [umoeller]
803 ULONG ulHours;
804 if (!(ulHours = pDateTime->hours % 12))
805 ulHours = 12;
806
807 // >= 12h: PM.
808 PrfQueryProfileString(HINI_USER,
809 "PM_National",
810 "s2359", // key
811 "PM", // default
812 szAMPM, sizeof(szAMPM)-1);
813 sprintf(pszTime, "%02d%c%02d%c%02d %s",
814 // leave 12 == 12 (not 0)
815 ulHours,
816 cTimeSep,
817 pDateTime->minutes,
818 cTimeSep,
819 pDateTime->seconds,
820 szAMPM);
821 }
822 else
823 {
824 // < 12h: AM
825 PrfQueryProfileString(HINI_USER,
826 "PM_National",
827 "s1159", // key
828 "AM", // default
829 szAMPM, sizeof(szAMPM)-1);
830 sprintf(pszTime, "%02d%c%02d%c%02d %s",
831 pDateTime->hours,
832 cTimeSep,
833 pDateTime->minutes,
834 cTimeSep,
835 pDateTime->seconds,
836 szAMPM);
837 }
838 }
839 else
840 // 24-hour clock
841 sprintf(pszTime, "%02d%c%02d%c%02d",
842 pDateTime->hours,
843 cTimeSep,
844 pDateTime->minutes,
845 cTimeSep,
846 pDateTime->seconds);
847 }
848}
849
850/*
851 * strhDateTime:
852 * wrapper around nlsDateTime for those who used
853 * the XFLDR.DLL export.
854 */
855
856VOID APIENTRY strhDateTime(PSZ pszDate, // out: date string returned (can be NULL)
857 PSZ pszTime, // out: time string returned (can be NULL)
858 DATETIME *pDateTime, // in: date/time information
859 ULONG ulDateFormat, // in: date format (0-3); see nlsFileDate
860 CHAR cDateSep, // in: date separator (e.g. '.')
861 ULONG ulTimeFormat, // in: 24-hour time format (0 or 1); see nlsFileTime
862 CHAR cTimeSep) // in: time separator (e.g. ':')
863{
864 nlsDateTime(pszDate,
865 pszTime,
866 pDateTime,
867 ulDateFormat,
868 cDateSep,
869 ulTimeFormat,
870 cTimeSep);
871}
872
873CHAR G_szUpperMap[257];
874BOOL G_fUpperMapInited = FALSE;
875
876/*
877 *@@ InitUpperMap:
878 * initializes the case map for nlsUpper.
879 *
880 *@@added V0.9.20 (2002-07-25) [umoeller]
881 */
882
883STATIC VOID InitUpperMap(VOID)
884{
885 ULONG ul;
886 COUNTRYCODE cc;
887 BOOL fDBCS = nlsDBCS();
888
889 for (ul = 0;
890 ul < 256;
891 ++ul)
892 {
893 G_szUpperMap[ul] = (CHAR)ul;
894
895 if ( (fDBCS)
896 && (G_afLeadByte[ul] != TYPE_SBCS)
897 )
898 G_szUpperMap[ul] = ' ';
899 }
900
901 G_szUpperMap[256] = '\0';
902
903 cc.country = 0; // use system country code
904 cc.codepage = 0; // use process default codepage
905 DosMapCase(255,
906 &cc,
907 G_szUpperMap + 1);
908
909 G_fUpperMapInited = TRUE;
910}
911
912/*
913 *@@ nlsUpper:
914 * quick hack for upper-casing a string.
915 *
916 * This now returns the length of the given string always
917 * (V0.9.20).
918 *
919 * Remarks:
920 *
921 * -- On the first call, we build a case map table by
922 * calling DosMapCase with the default system country
923 * code and the process's codepage. Since DosMapCase
924 * is terribly slow with all the 16-bit thunking
925 * involved, we can then use our table without having
926 * to use the API ever again.
927 *
928 * -- As a result, if the process codepage changes for
929 * any reason, this function will not pick up the
930 * change.
931 *
932 * -- This has provisions for DBCS, which hopefully
933 * work.
934 *
935 *@@added V0.9.16 (2001-10-25) [umoeller]
936 *@@changed V0.9.20 (2002-07-25) [umoeller]: speedup, changed prototype
937 *@@changed XWP V1.0.8 (2008-05-25) [pr]: rewritten for correct DBCS operation @@fixes 1070
938 */
939
940ULONG nlsUpper(PSZ psz) // in/out: string
941{
942 BOOL bDBCSType = TYPE_SBCS;
943 ULONG ul = 0;
944 PSZ p = psz;
945
946 if (!G_fUpperMapInited)
947 InitUpperMap();
948
949 for (; p && *p; p++, ul++)
950 {
951 switch(bDBCSType)
952 {
953 case TYPE_SBCS:
954 case TYPE_DBCS_2ND:
955 bDBCSType = G_afLeadByte[*p];
956 break;
957
958 case TYPE_DBCS_1ST :
959 bDBCSType = TYPE_DBCS_2ND;
960 break;
961 }
962
963 if (bDBCSType == TYPE_SBCS)
964 *p = G_szUpperMap[*p];
965 }
966
967 return ul;
968}
969
Note: See TracBrowser for help on using the repository browser.