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

Last change on this file since 350 was 349, checked in by pr, 18 years ago

Load UCONV and LIBUNI functions dynamically. Bug 936.

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