source: branches/libc-0.6/src/emx/src/lib/locale/setlocale.c@ 3788

Last change on this file since 3788 was 3788, checked in by bird, 13 years ago

setlocale.c: Changed getCodepage to default to the process codepage instead of the one specified by the locale as this cause real life trouble otherwise. Fixes #227, #240, #187.

  • Property cvs2svn:cvs-rev set to 1.27
  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
File size: 66.7 KB
Line 
1/* $Id: setlocale.c 3788 2012-03-22 20:12:40Z bird $ */
2/** @file
3 *
4 * Locale support implementation through OS/2 Unicode API.
5 *
6 * Implementation of the setlocale() function.
7 *
8 *
9 * Copyright (c) 2003 InnoTek Systemberatung GmbH
10 * Copyright (c) 2004 knut st. osmundsen <bird-srcspam@anduin.net>
11 *
12 *
13 * This file is part of InnoTek LIBC.
14 *
15 * InnoTek LIBC is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU Lesser General Public License as published
17 * by the Free Software Foundation; either version 2 of the License, or
18 * (at your option) any later version.
19 *
20 * InnoTek LIBC is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU Lesser General Public License for more details.
24 *
25 * You should have received a copy of the GNU Lesser General Public License
26 * along with InnoTek LIBC; if not, write to the Free Software
27 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 *
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#include "libc-alias.h"
36#include <InnoTekLIBC/locale.h> /* must be included before ctype. */
37#include <ctype.h>
38
39#include <stdlib.h>
40
41#include <alloca.h>
42#include <string.h>
43#include <errno.h>
44#include <386/builtin.h>
45#include <sys/param.h>
46#include <sys/smutex.h>
47#include <InnoTekLIBC/errno.h>
48#include <InnoTekLIBC/fork.h>
49
50#define INCL_DOS
51#define INCL_FSMACROS
52#include <os2emx.h>
53#include <unidef.h>
54#include <uconv.h>
55
56#define __LIBC_LOG_GROUP __LIBC_LOG_GRP_LOCALE
57#include <InnoTekLIBC/logstrict.h>
58
59
60/*******************************************************************************
61* Defined Constants And Macros *
62*******************************************************************************/
63/** Instead of strcmp since it's faster. */
64#define IS_C_LOCALE(s) (((s)[0] == 'C') && (!(s)[1]))
65/** For simplisity. */
66#define IS_POSIX_LOCALE(s) ( (s)[0] == 'P' \
67 && (s)[1] == 'O' \
68 && (s)[2] == 'S' \
69 && (s)[3] == 'I' \
70 && (s)[4] == 'X' \
71 && (s)[5] == '\0' )
72#define IS_EURO(s) (( (s)[0] == 'E' \
73 && (s)[1] == 'U' \
74 && (s)[2] == 'R' \
75 && (s)[3] == 'O' \
76 && (s)[4] == '\0' \
77 ) || ( \
78 (s)[0] == 'e' \
79 && (s)[1] == 'u' \
80 && (s)[2] == 'r' \
81 && (s)[3] == 'o' \
82 && (s)[4] == '\0') )
83/** Max lenght we anticipate of a codepage name. */
84#define CODEPAGE_MAX_LENGTH 64
85
86/*******************************************************************************
87* Structures and Typedefs *
88*******************************************************************************/
89/** Structure used while sorting codepage characters by their weights. */
90struct __collate_weight
91{
92 /* Character code. */
93 unsigned char code;
94 /* Actual weight length. */
95 unsigned char len;
96 /* The weight itself. */
97 UniChar ucsWeight[7];
98};
99
100
101/**
102 * There is one global object of this type that contains integral
103 * information about last selected (with setlocale()) locale.
104 * The locale information itself is split into parts to avoid linking
105 * unused data into programs that use just the "C" locale and just
106 * a few functions that use locale data (such as strdate()).
107 */
108typedef struct __libc_LocaleGlobal
109{
110 /** Category names. */
111 char *apszNames[_LC_LAST + 1];
112 /* Lock for multi-threaded operations. */
113 _smutex lock;
114} __LIBC_LOCALEGLOBAL, *__LIBC_PLOCALEGLOBAL;
115
116
117/**
118 * Internal working structure which we modify while
119 * performing the setlocale() operation.
120 */
121struct temp_locale
122{
123 /** Which we have processed.*/
124 int afProcessed[_LC_LAST + 1];
125 /** The global data. */
126 __LIBC_LOCALEGLOBAL Global;
127 /** Collate data. */
128 __LIBC_LOCALECOLLATE Collate;
129 /** Ctype data. */
130 __LIBC_LOCALECTYPE Ctype;
131 /** Ctype conversion functions. */
132 __LIBC_PCLOCALECTYPEFUNCS pCtypeFuncs;
133 /** Time data. */
134 __LIBC_LOCALETIME Time;
135 /** Numeric and monetary data. */
136 __LIBC_LOCALELCONV Lconv;
137 /** Messages data. */
138 __LIBC_LOCALEMSG Msg;
139};
140
141
142/*******************************************************************************
143* Global Variables *
144*******************************************************************************/
145/** Array of local categories. Use their defined value + 1 as index (LC_ALL is -1). */
146static const char gaszCategories[_LC_LAST + 1][16] =
147{
148 "LC_ALL", /* -1 */
149 "LC_COLLATE", /* 0 */
150 "LC_CTYPE",
151 "LC_MONETARY",
152 "LC_NUMERIC",
153 "LC_TIME",
154 "LC_MESSAGES"
155};
156/** Array of the lengths corresponding to the entries in the above array. */
157static const unsigned char gacchCategories[_LC_LAST + 1] =
158{
159 sizeof("LC_ALL") - 1,
160 sizeof("LC_COLLATE") - 1,
161 sizeof("LC_CTYPE") - 1,
162 sizeof("LC_MONETARY") - 1,
163 sizeof("LC_NUMERIC") - 1,
164 sizeof("LC_TIME") - 1,
165 sizeof("LC_MESSAGES") - 1
166};
167
168/** "C" string. */
169static const char gszC[] = "C";
170
171/** "POSIX" string. */
172static const char gszPOSIX[] = "POSIX";
173
174/** The currnet locale specifications. */
175static __LIBC_LOCALEGLOBAL gLocale =
176{
177 .apszNames =
178 {
179 (char *)gszC, /* LC_ALL */
180 (char *)gszC, /* LC_COLLATE */
181 (char *)gszC, /* LC_CTYPE */
182 (char *)gszC, /* LC_NUMERIC */
183 (char *)gszC, /* LC_MONETARY */
184 (char *)gszC, /* LC_TIME */
185 (char *)gszC /* LC_MESSAGES */
186 },
187 .lock = 0
188};
189
190/** "ISO8859-1" UniChar string. */
191static const UniChar gucsISO8859_1[] = {'I', 'S', 'O', '8', '8', '5', '9', '-', '1', '\0' };
192
193/** @page pg_env
194 * @subsection pg_env_sub1_LIBC_SETLOCALE_OLDSTYLE LIBC_SETLOCALE_OLDSTYLE
195 *
196 * When the LIBC_SETLOCALE_OLDSTYLE environment variable is present in the
197 * environemnt LIBC will ask OS/2 about the country and codepage so the right
198 * default locale can be found when none of the POSIX variables are present.
199 *
200 * The default behaviour (i.e. when LIBC_SETLOCALE_OLDSTYLE is not present) is
201 * to use the 'C' locale if none of the LANG or LC_* variables can be found.
202 */
203
204/** Whether or not to use the old style where we query extra stuff from OS/2.
205 * The new style is more conforming to POSIX and with VAC.
206 * The presense of LIBC_SETLOCALE_OLDSTYLE forces the old style.
207 *
208 * If the value is negative Then init is required.
209 */
210static int gfOldStyle = -1;
211
212
213/*******************************************************************************
214* Internal Functions *
215*******************************************************************************/
216static int unierr2errno(int rc);
217static int convert_ucs(UconvObject uobj, UniChar *in, char **out);
218static void Ucs2Sb(UniChar *ucs, char *sbs, size_t cch);
219static const char *getDefaultLocale(const char *pszCategory, char *pszBuffer, int *pfDefault);
220static int getCodepage(const char *pszCodepage, const char *pszLocale, LocaleObject lobj, UniChar *pucsCodepage, unsigned cucCodepage);
221static int query_mbcs(UconvObject uobj, char *mbcs, unsigned char *au2MBCSPrefixs);
222
223static int localeCollateDo(__LIBC_PLOCALECOLLATE pCollate, UconvObject uobj, LocaleObject lobj, const char *pszLocale);
224static void localeCollateFree(__LIBC_PLOCALECOLLATE pCollate);
225static inline unsigned char Transform(LocaleObject lobj, UconvObject uobj,
226 UniChar (*pfnTransFunc) (LocaleObject, UniChar),
227 UniChar uc, unsigned char uchFallback);
228static int localeCtypeDo(__LIBC_PLOCALECTYPE pCtype, UconvObject uobj, LocaleObject lobj, const char *pszLocale, const char *pszCodeset);
229static void localeCtypeFree(__LIBC_PLOCALECTYPE pCtype);
230static int query_item(LocaleObject lobj, UconvObject uobj, LocaleItem iItem, char **ppszOut);
231static int query_array(LocaleObject lobj, UconvObject uobj, int cElements, LocaleItem iFirst, char **papszOut);
232static int localeTimeDo(__LIBC_PLOCALETIME pTime, UconvObject uobj, LocaleObject lobj, const char *pszLocale);
233static void localeTimeFree(__LIBC_PLOCALETIME pTime);
234static void localeNumericFree(__LIBC_PLOCALELCONV pLconv);
235static void localeMonetaryFree(__LIBC_PLOCALELCONV pLconv);
236static int localeNumericDo(__LIBC_PLOCALELCONV pLconv, UconvObject uobj, struct UniLconv *pULconv, const char *pszLocale);
237static int localeMonetaryDo(__LIBC_PLOCALELCONV pLconv, UconvObject uobj, LocaleObject lobj, struct UniLconv *pULconv, const char *pszLocale, const char *pszModifier);
238static void localeGlobalFree(__LIBC_PLOCALEGLOBAL pGlobal, int iCategory);
239static int localeParseLocale(char *pszLocale, const char **ppszCodepage, const char **ppszModifier);
240static int localeDoOne(struct temp_locale *pTemp, int iCategory, const char *pszLocale, const char *pszCodepage, const char *pszModifier);
241static int localeDo(struct temp_locale *pTemp, int iCategory, char *pszLocale, int fDefaultValue);
242static char *localeCommit(struct temp_locale *pTemp, int iCategory);
243static void localeFree(struct temp_locale *pTemp);
244
245static int setlocalForkChild1(__LIBC_PFORKHANDLE pForkHandle, __LIBC_FORKOP enmOperation);
246
247
248
249
250
251
252/**
253 * Converts from Uni api error code to errno.
254 *
255 * @returns errno
256 * @param rc Uni api error code.
257 */
258static int unierr2errno(int rc)
259{
260 switch (rc)
261 {
262 case ULS_SUCCESS: return 0;
263 case ULS_MAXFILESPERPROC: return -EMFILE;
264 case ULS_MAXFILES: return -ENFILE;
265 case ULS_BADOBJECT: return -EBADF;
266 case ULS_BADHANDLE: return -EBADF;
267 case ULS_NOTIMPLEMENTED: return -ENOSYS;
268 case ULS_RANGE: return -ERANGE;
269 case ULS_NOMEMORY: return -ENOMEM;
270 case ULS_OTHER:
271 case ULS_ILLEGALSEQUENCE:
272 case ULS_NOOP:
273 case ULS_TOOMANYKBD:
274 case ULS_KBDNOTFOUND:
275 case ULS_NODEAD:
276 case ULS_NOSCAN:
277 case ULS_INVALIDSCAN:
278 case ULS_INVALID:
279 case ULS_NOTOKEN:
280 case ULS_NOMATCH:
281 case ULS_BUFFERFULL:
282 case ULS_UNSUPPORTED:
283 case ULS_BADATTR:
284 case ULS_VERSION:
285 default:
286 return -EINVAL;
287 }
288}
289
290static int convert_ucs(UconvObject uobj, UniChar *in, char **out)
291{
292 size_t usl = UniStrlen (in) + 1;
293 /* Allocate twice as much as we need - just in case every character is DBCS
294 or desired encoding is UCS-2. */
295 size_t osl = usl * 2;
296 size_t in_left = usl;
297 size_t nonid, out_left = osl;
298 char *tmp = malloc (osl);
299 UniChar *inbuf = in;
300 void *outbuf = tmp;
301 int try_count = 0;
302 FS_VAR();
303
304 FS_SAVE_LOAD();
305 try_again:
306
307 if (try_count > 10)
308 {
309 /* Well... nobody will say we gave it no chance ... */
310 free(tmp);
311 FS_RESTORE();
312 return -1;
313 }
314
315 switch (UniUconvFromUcs (uobj, &inbuf, &in_left, &outbuf, &out_left, &nonid))
316 {
317 case 0:
318 break;
319
320 case UCONV_E2BIG:
321 /* Out buffer too small, make one larger */
322 inbuf = in; in_left = usl;
323 out_left = (osl *= 2);
324 outbuf = tmp = realloc (tmp, osl);
325 try_count++;
326 goto try_again;
327
328 default:
329 /* Unexpected error. */
330 free (tmp);
331 FS_RESTORE();
332 return -1;
333 }
334
335 usl = (char *)outbuf - (char *)tmp;
336 (*out) = (char *)malloc (usl);
337 memcpy (*out, tmp, usl);
338 free (tmp);
339 FS_RESTORE();
340
341 return 0;
342}
343
344static void Ucs2Sb(UniChar *ucs, char *sbs, size_t cch)
345{
346 while (cch--)
347 *sbs++ = *ucs++;
348}
349
350
351static int query_mbcs(UconvObject uobj, char *mbcs, unsigned char *au2MBCSPrefixs)
352{
353 unsigned i;
354 uconv_attribute_t uconv_attr;
355 unsigned char uchSeqlen[256];
356 int rc;
357
358 /*
359 * Query data.
360 */
361 rc = UniQueryUconvObject(uobj, &uconv_attr, sizeof(uconv_attr), (char *)&uchSeqlen[0], NULL, NULL);
362 if (rc)
363 return -unierr2errno(rc);
364
365 /*
366 * Create the return values.
367 */
368 *mbcs = uconv_attr.mb_max_len > 1 ? uconv_attr.mb_max_len : 0;
369
370 bzero(au2MBCSPrefixs, 256/4);
371 for (i = 0; i < 256; i++)
372 if (uchSeqlen[i] != 255)
373 SET_MBCS_PREFIX(au2MBCSPrefixs, i, uchSeqlen[i]);
374
375 return 0;
376}
377
378/**
379 * Sets the LC_COLLATE part of the locale.
380 *
381 * @returns 0 on success.
382 * @returns negated errno on failure.
383 * @param pCollate The collate structure to operate on.
384 * @param uobj The UconvObject to use. Collate is responsible for freeing it.
385 * @param lobj The LocaleObject to use. Collate is responsible for freeing it.
386 * @param pszLocale Pointer to the locale base specifier.
387 */
388static int localeCollateDo(__LIBC_PLOCALECOLLATE pCollate, UconvObject uobj, LocaleObject lobj, const char *pszLocale)
389{
390 int rc;
391
392 /* Cleanup in case of some special LC_ALL call. */
393 localeCollateFree(pCollate);
394
395 /*
396 * For "C" / "POSIX" we can simply use the static default locale.
397 */
398 if ( IS_C_LOCALE(pszLocale)
399 || IS_POSIX_LOCALE(pszLocale))
400 {
401 memcpy(pCollate, &__libc_gLocaleCollateDefault, sizeof(*pCollate));
402 return 0;
403 }
404
405 /*
406 * Query multi-byte related stuff.
407 */
408 rc = query_mbcs(uobj, &pCollate->mbcs, &pCollate->au2MBCSPrefixs[0]);
409 if (rc)
410 return rc;
411
412 if (pCollate->mbcs)
413 {
414 /*
415 * In MBCS mode we just borrow the conversion and locale objects
416 * and leave the real work to the Unicode subsystem.
417 */
418 pCollate->lobj = lobj;
419 pCollate->uobj = uobj;
420 }
421 else
422 {
423 /*
424 * In SBCS we query the weight of every character and use the
425 * weights directly, without the need to invoke the Unicode API.
426 */
427 struct __collate_weight aCW[256];
428 int i, c;
429
430 /* Initialize character weights. */
431 for (i = 0; i < 256; i++)
432 {
433 UniChar ucs[2];
434 if (!__libc_ucs2To(uobj, (unsigned char *)&i, 1, &ucs[0]))
435 {
436 LIBCLOG_ERROR2("__libc_ucs2To failed for char %d\n", i);
437 ucs[0] = (UniChar)i;
438 }
439 ucs[1] = '\0';
440
441 aCW[i].code = i;
442 aCW[i].len = UniStrxfrm(lobj, aCW[i].ucsWeight, &ucs[0], sizeof(aCW[i].ucsWeight) / sizeof(aCW[i].ucsWeight[0]));
443 if (aCW[i].len >= sizeof(aCW[i].ucsWeight) / sizeof(aCW[i].ucsWeight[0]))
444 {
445 LIBC_ASSERTM_FAILED("This cannot happen... :-) i=%d len=%d \n", i, aCW[i].len);
446 aCW[i].len = sizeof(aCW[i].ucsWeight) / sizeof(aCW[i].ucsWeight[0]);
447 }
448
449 /* Workaround for bug / undocumented UniStrxfrm feature. See strxfrm.c & mozilla sources. */
450 aCW[i].len = MIN(aCW[i].len * 2, sizeof(aCW[i].ucsWeight) / sizeof(aCW[i].ucsWeight[0]));
451 while (!aCW[i].ucsWeight[aCW[i].len - 1])
452 aCW[i].len--;
453 }
454
455 /*
456 * Do bubble sorting since qsort() doesn't guarantee that the order
457 * of equal elements stays the same.
458 */
459 c = 256 - 1;
460 do
461 {
462 int j = 0;
463 for (i = 0; i < c; i++)
464 if (UniStrncmp(aCW[i].ucsWeight, aCW[i + 1].ucsWeight, MIN(aCW[i].len, aCW[i + 1].len)) > 0)
465 {
466 _memswap(&aCW[i], &aCW[i + 1], sizeof(aCW[i]));
467 j = i;
468 }
469 c = j;
470 } while (c);
471
472 /*
473 * Store the result.
474 */
475 for (i = 0; i < 256; i++)
476 pCollate->auchWeight[aCW[i].code] = i;
477
478 /* cleanup */
479 UniFreeUconvObject(uobj);
480 UniFreeLocaleObject(lobj);
481 }
482
483 return 0;
484}
485
486/**
487 * Frees system and CRT resources assocated with a collate structure.
488 * @param pCollate The collate structure.
489 */
490static void localeCollateFree(__LIBC_PLOCALECOLLATE pCollate)
491{
492 /* Free old pszLocale objects, if any */
493 if (pCollate->uobj)
494 {
495 UniFreeUconvObject(pCollate->uobj);
496 pCollate->uobj = NULL;
497 }
498 if (pCollate->lobj)
499 {
500 UniFreeLocaleObject(pCollate->lobj);
501 pCollate->lobj = NULL;
502 }
503}
504
505static inline unsigned char Transform(LocaleObject lobj, UconvObject uobj,
506 UniChar (* APIENTRY pfnTransFunc) (LocaleObject, UniChar),
507 UniChar uc, unsigned char uchFallback)
508{
509 unsigned char sbcs;
510 int nb = __libc_ucs2From(uobj, pfnTransFunc(lobj, uc), &sbcs, 1);
511 return (nb == 1) ? sbcs : uchFallback;
512}
513
514
515/**
516 * Sets the LC_TYPE part of the locale.
517 *
518 * @returns 0 on success.
519 * @returns negated errno on failure.
520 * @param pCtype The Ctype structure to operate on.
521 * @param uobj The UconvObject to use. Ctype is responsible for freeing it.
522 * @param lobj The LocaleObject to use. Ctype is responsible for freeing it.
523 * @param pszLocale Pointer to the locale base specifier.
524 * @param pszCodeset The codeset used.
525 */
526static int localeCtypeDo(__LIBC_PLOCALECTYPE pCtype, UconvObject uobj, LocaleObject lobj, const char *pszLocale, const char *pszCodeset)
527{
528 int rc;
529 unsigned i;
530
531 /* Cleanup in case of some special LC_ALL call. */
532 localeCtypeFree(pCtype);
533
534 /*
535 * For "C" / "POSIX" we can simply use the static default locale.
536 */
537 if ( IS_C_LOCALE(pszLocale)
538 || IS_POSIX_LOCALE(pszLocale))
539 {
540 memcpy(pCtype, &__libc_GLocaleCtypeDefault, sizeof(*pCtype));
541 return 0;
542 }
543
544 /*
545 * Query multi-byte related stuff.
546 */
547 rc = query_mbcs(uobj, &pCtype->mbcs, &pCtype->au2MBCSPrefixs[0]);
548 if (rc)
549 return rc;
550
551 /*
552 * Set codeset and query encoding.
553 */
554 strncpy(pCtype->szCodeSet, pszCodeset, sizeof(pCtype->szCodeSet));
555 pCtype->szCodeSet[sizeof(pCtype->szCodeSet) - 1] = '\0';
556
557 uconv_attribute_t attr;
558 rc = UniQueryUconvObject(uobj, &attr, sizeof(attr), NULL, NULL, NULL);
559 if (rc)
560 return -unierr2errno(rc);
561 switch (attr.esid)
562 {
563 case ESID_sbcs_data:
564 case ESID_sbcs_pc:
565 case ESID_sbcs_ebcdic:
566 case ESID_sbcs_iso:
567 case ESID_sbcs_windows:
568 case ESID_sbcs_alt:
569 __libc_localeFuncsSBCS(&pCtype->CtypeFuncs);
570 break;
571
572 case ESID_dbcs_data:
573 case ESID_dbcs_pc:
574 case ESID_dbcs_ebcdic:
575 __libc_localeFuncsDBCS(&pCtype->CtypeFuncs);
576 break;
577
578 case ESID_mbcs_data:
579 case ESID_mbcs_pc:
580 case ESID_mbcs_ebcdic:
581 __libc_localeFuncsMBCS(&pCtype->CtypeFuncs);
582 break;
583
584 case ESID_ucs_2:
585 __libc_localeFuncsUCS2(&pCtype->CtypeFuncs);
586 break;
587
588 case ESID_utf_8:
589 __libc_localeFuncsUTF8(&pCtype->CtypeFuncs);
590 break;
591
592 case ESID_upf_8:
593 case ESID_ugl:
594 default:
595 __libc_localeFuncsDefault(&pCtype->CtypeFuncs);
596 break;
597 }
598
599 /*
600 * For speeding up isXXX() and lower/upper case mapping functions
601 * we'll cache the type of every character into a variable.
602 *
603 * Do every character separately to avoid errors that could result
604 * when some character in the middle cannot be conerted from or to
605 * Unicode - this would lead in a shift of the entire string.
606 */
607 /** @todo Aren't there apis for getting all this at once? */
608 bzero(pCtype->auchToSBCS0To128, sizeof(pCtype->auchToSBCS0To128));
609 bzero(pCtype->aSBCSs, sizeof(pCtype->aSBCSs));
610 pCtype->cSBCSs = 0;
611 for (i = 0; i < 256; i++)
612 {
613 unsigned ufType = 0;
614 unsigned char uchUpper = i;
615 unsigned char uchLower = i;
616 UniChar uc = 0xffff;
617
618 /* isxxx() does not support MBCS characters at all. */
619 if (!pCtype->mbcs || !IS_MBCS_PREFIX(pCtype, i))
620 {
621 if (__libc_ucs2To(uobj, (unsigned char *)&i, 1, &uc))
622 {
623 /* ASSUMES that lower/upper are paired! */
624 /* ASSUMES that there are no difference between the locale and the unicode spec! */
625 UNICTYPE *pUniType = UniQueryCharType(uc);
626 if (pUniType)
627 {
628 ufType = ___wctype_uni(pUniType, uc);
629 if (ufType & __CT_LOWER)
630 uchUpper = Transform(lobj, uobj, UniTransUpper, uc, i);
631 if (ufType & __CT_UPPER)
632 uchLower = Transform(lobj, uobj, UniTransLower, uc, i);
633
634 /*
635 * Add to conversion table.
636 */
637 if (uc < 128)
638 pCtype->auchToSBCS0To128[uc] = i;
639 else
640 {
641 /*
642 * Try fit it into an existing chunk.
643 */
644 int iChunk;
645 for (iChunk = 0; iChunk < pCtype->cSBCSs; iChunk++)
646 {
647 int cFree = sizeof(pCtype->aSBCSs[iChunk].auch) / sizeof(pCtype->aSBCSs[iChunk].auch[0]) - pCtype->aSBCSs[iChunk].cChars;
648 if (cFree > 0)
649 {
650 int off = (int)uc - (int)pCtype->aSBCSs[iChunk].usStart;
651 if (off < sizeof(pCtype->aSBCSs[iChunk].auch) / sizeof(pCtype->aSBCSs[iChunk].auch[0]))
652 {
653 if (off >= 0)
654 {
655 if (pCtype->aSBCSs[iChunk].cChars <= off)
656 pCtype->aSBCSs[iChunk].cChars = off + 1;
657 pCtype->aSBCSs[iChunk].auch[off] = i;
658 break;
659 }
660 /* Relocate the chunk up to 3 bytes if that will help. (might cause overlapping areas!) */
661 else if (off >= -3 && -off < cFree)
662 {
663 off = -off;
664 memmove(&pCtype->aSBCSs[iChunk].auch[off], &pCtype->aSBCSs[iChunk].auch[0], pCtype->aSBCSs[iChunk].cChars * sizeof(pCtype->aSBCSs[iChunk].auch[0]));
665
666 pCtype->aSBCSs[iChunk].usStart -= off;
667 pCtype->aSBCSs[iChunk].cChars += off;
668 pCtype->aSBCSs[iChunk].auch[0] = i;
669 switch (off)
670 {
671 case 3: pCtype->aSBCSs[iChunk].auch[2] = 0;
672 case 2: pCtype->aSBCSs[iChunk].auch[1] = 0; break;
673 }
674 break;
675 }
676 }
677 }
678 } /* foreach chunk */
679
680 /*
681 * Add new chunk?
682 */
683 if ( iChunk == pCtype->cSBCSs
684 && iChunk < sizeof(pCtype->aSBCSs) / sizeof(pCtype->aSBCSs[0]))
685 {
686 pCtype->aSBCSs[iChunk].usStart = uc;
687 pCtype->aSBCSs[iChunk].cChars = 1;
688 pCtype->aSBCSs[iChunk].auch[0] = i;
689 pCtype->cSBCSs++;
690 }
691 }
692 } /* Unicode type data, ok. */
693 }
694 else
695 uc = 0xffff;
696 }
697
698 /* Store the data in the locale structure. */
699 pCtype->aufType[i] = ufType;
700 pCtype->auchUpper[i] = uchUpper;
701 pCtype->auchLower[i] = uchLower;
702 pCtype->aucUnicode[i] = uc;
703 } /* foreach char 0..255 */
704
705 /*
706 * Store the objects.
707 */
708 pCtype->uobj = uobj;
709 if (pCtype->mbcs)
710 {
711 /*
712 * In MBCS mode we just borrow the local object and leave the
713 * real work to the Unicode subsystem.
714 */
715 pCtype->lobj = lobj;
716 }
717 else
718 UniFreeLocaleObject(lobj);
719
720 return 0;
721}
722
723
724/**
725 * Frees system and CRT resources assocated with a ctype structure.
726 * @param pCtype The collate structure.
727 */
728static void localeCtypeFree(__LIBC_PLOCALECTYPE pCtype)
729{
730 if (pCtype->uobj)
731 {
732 UniFreeUconvObject(pCtype->uobj);
733 pCtype->uobj = NULL;
734 }
735 if (pCtype->lobj)
736 {
737 UniFreeLocaleObject(pCtype->lobj);
738 pCtype->lobj = NULL;
739 }
740}
741
742/**
743 * Query one item from a locale object.
744 */
745static int query_item(LocaleObject lobj, UconvObject uobj, LocaleItem iItem, char **ppszOut)
746{
747 /*
748 * Query item.
749 */
750 UniChar *pucsItem;
751 int rc = UniQueryLocaleItem(lobj, iItem, &pucsItem);
752 if (rc)
753 {
754 LIBC_ASSERTM_FAILED("UniQueryLocaleItem(,%d,) -> rc=%d\n", iItem, rc);
755 return -unierr2errno(rc);
756 }
757
758 /*
759 * Convert from Ucs2.
760 */
761 rc = convert_ucs(uobj, pucsItem, ppszOut);
762
763 UniFreeMem(pucsItem);
764 return rc;
765}
766
767/**
768 * Query an array of locale string items.
769 */
770static int query_array(LocaleObject lobj, UconvObject uobj, int cElements, LocaleItem iFirst, char **papszOut)
771{
772 int i;
773 for (i = 0; i < cElements; i++)
774 {
775 int rc = query_item(lobj, uobj, iFirst + i, &papszOut[i]);
776 if (rc)
777 return rc;
778 }
779 return 0;
780}
781
782/**
783 * Sets the LC_TIME part of the locale.
784 *
785 * @returns 0 on success.
786 * @returns negated errno on failure.
787 * @param pTime The time structure to operate on.
788 * @param uobj The UconvObject to use.
789 * @param lobj The LocaleObject to use.
790 * @param pszLocale Pointer to the locale base specifier.
791 */
792static int localeTimeDo(__LIBC_PLOCALETIME pTime, UconvObject uobj, LocaleObject lobj, const char *pszLocale)
793{
794 int rc;
795
796 /* free old stuff. */
797 localeTimeFree(pTime);
798
799 /*
800 * For "C" / "POSIX" we can simply use the static default locale.
801 */
802 if ( IS_C_LOCALE(pszLocale)
803 || IS_POSIX_LOCALE(pszLocale))
804 {
805 memcpy(pTime, &__libc_gLocaleTimeDefault, sizeof(*pTime));
806 return 0;
807 }
808
809 /* query the items. */
810 if ( (rc = query_item( lobj, uobj, D_T_FMT, &pTime->date_time_fmt))
811 || (rc = query_item( lobj, uobj, D_FMT, &pTime->date_fmt))
812 || (rc = query_item( lobj, uobj, T_FMT, &pTime->time_fmt))
813 || (rc = query_item( lobj, uobj, AM_STR, &pTime->am))
814 || (rc = query_item( lobj, uobj, PM_STR, &pTime->pm))
815 || (rc = query_item( lobj, uobj, T_FMT_AMPM, &pTime->ampm_fmt))
816 || (rc = query_item( lobj, uobj, ERA, &pTime->era))
817 || (rc = query_item( lobj, uobj, ERA_D_FMT, &pTime->era_date_fmt))
818 || (rc = query_item( lobj, uobj, ERA_D_T_FMT, &pTime->era_date_time_fmt))
819 || (rc = query_item( lobj, uobj, ERA_T_FMT, &pTime->era_time_fmt))
820 || (rc = query_item( lobj, uobj, ALT_DIGITS, &pTime->alt_digits))
821 || (rc = query_item( lobj, uobj, DATESEP, &pTime->datesep))
822 || (rc = query_item( lobj, uobj, TIMESEP, &pTime->timesep))
823 || (rc = query_item( lobj, uobj, LISTSEP, &pTime->listsep))
824 || (rc = query_array(lobj, uobj, 7, DAY_1, &pTime->lwdays[0]))
825 || (rc = query_array(lobj, uobj, 7, ABDAY_1, &pTime->swdays[0]))
826 || (rc = query_array(lobj, uobj, 12, MON_1, &pTime->lmonths[0]))
827 || (rc = query_array(lobj, uobj, 12, ABMON_1, &pTime->smonths[0]))
828 )
829 {
830 return rc;
831 }
832
833 /*
834 * Hacks for bad data in LOCALE.DLL.
835 */
836 if (!strcmp(pTime->date_time_fmt, "%a %e %b %H:%H:%S %Z %Y"))
837 strcpy( pTime->date_time_fmt, "%a %e %b %H:%M:%S %Z %Y");
838
839 return 0;
840}
841
842/**
843 * Frees the CRT resources held up by a time structure.
844 * @param pTime The time structure.
845 */
846static void localeTimeFree(__LIBC_PLOCALETIME pTime)
847{
848 if (!pTime->fConsts)
849 {
850 /*
851 * Everything is pointers to heap here!
852 */
853 char **ppsz = (char **)pTime;
854 char **ppszEnd = (char **)pTime->fConsts;
855 while (ppsz < ppszEnd)
856 {
857 void *pv = *ppsz;
858 if (pv)
859 {
860 free(pv);
861 *ppsz = NULL;
862 }
863 }
864 }
865 pTime->fConsts = 0;
866}
867
868
869/**
870 * Frees all heap strings in the monetary part of the lconv structure.
871 * @param pLconv What to work on.
872 */
873static void localeNumericFree(__LIBC_PLOCALELCONV pLconv)
874{
875#define FREE(x) do { if (pLconv->s.x && !pLconv->fNumericConsts) free(pLconv->s.x); pLconv->s.x = NULL; } while (0)
876 FREE(decimal_point);
877 FREE(thousands_sep);
878 FREE(grouping);
879
880 pLconv->fNumericConsts = 0;
881#undef FREE
882}
883
884/**
885 * Frees all heap strings in the monetary part of the lconv structure.
886 * @param pLconv What to work on.
887 */
888static void localeMonetaryFree(__LIBC_PLOCALELCONV pLconv)
889{
890#define FREE(x) do { if (pLconv->x && !pLconv->fMonetaryConsts) free(pLconv->x); pLconv->x = NULL; } while (0)
891 FREE(s.int_curr_symbol);
892 FREE(s.currency_symbol);
893 FREE(s.mon_decimal_point);
894 FREE(s.mon_thousands_sep);
895 FREE(s.mon_grouping);
896 FREE(s.positive_sign);
897 FREE(s.negative_sign);
898 FREE(pszCrncyStr);
899
900 pLconv->fMonetaryConsts = 0;
901#undef FREE
902}
903
904/**
905 * Converts a grouping array.
906 */
907static int localeConvertGrouping(short *pasGrouping, char **pachRet)
908{
909 short *ps;
910 char *pch;
911 int cch;
912
913 for (cch = 1, ps = pasGrouping; *ps && *ps != -1; ps++)
914 cch++;
915 *pachRet = pch = malloc(cch);
916 if (!pch)
917 return -ENOMEM;
918 for (ps = pasGrouping; cch > 0; cch--)
919 *pch++ = (char)*ps++;
920
921 return 0;
922}
923
924/**
925 * Sets the LC_NUMERIC part of the locale.
926 *
927 * @returns 0 on success.
928 * @returns negated errno on failure.
929 * @param pLconv The lconv structure to operate on.
930 * @param uobj The UconvObject to use.
931 * @param pULconv Pointer to the pULconv structure to get the data from.
932 * @param pszLocale Pointer to the locale base specifier.
933 */
934static int localeNumericDo(__LIBC_PLOCALELCONV pLconv, UconvObject uobj, struct UniLconv *pULconv, const char *pszLocale)
935{
936 int rc;
937
938 /* free any old stuff. */
939 localeNumericFree(pLconv);
940
941 /*
942 * For "C" / "POSIX" we can simply use the static default locale.
943 */
944 if ( IS_C_LOCALE(pszLocale)
945 || IS_POSIX_LOCALE(pszLocale))
946 {
947#define COPY(m) do { pLconv->m = __libc_gLocaleLconvDefault.m; } while (0)
948 pLconv->fNumericConsts = 1;
949 COPY(s.decimal_point);
950 COPY(s.thousands_sep);
951 COPY(s.grouping);
952#undef COPY
953 return 0;
954 }
955
956 /*
957 * Convert the stuff.
958 */
959#define CONVERT_UCS(field) \
960 do { rc = convert_ucs(uobj, pULconv->field, &pLconv->s.field); if (rc) return rc; } while (0)
961 CONVERT_UCS(decimal_point);
962 CONVERT_UCS(thousands_sep);
963#undef CONVERT_UCS
964
965 return localeConvertGrouping(pULconv->grouping, &pLconv->s.grouping);
966}
967
968
969/**
970 * Sets the LC_MONETARY part of the locale.
971 *
972 * @returns 0 on success.
973 * @returns negated errno on failure.
974 * @param pLconv The lconv structure to operate on.
975 * @param uobj The UconvObject to use.
976 * @param pULconv Pointer to the pULconv structure to get the data from.
977 * @param pszLocale The locale base specifier.
978 * @param pszModifier The locale modifier.
979 */
980static int localeMonetaryDo(__LIBC_PLOCALELCONV pLconv, UconvObject uobj, LocaleObject lobj, struct UniLconv *pULconv, const char *pszLocale, const char *pszModifier)
981{
982 int rc;
983
984 /* free any old stuff. */
985 localeMonetaryFree(pLconv);
986
987 /*
988 * For "C" / "POSIX" we can simply use the static default locale.
989 */
990 if ( IS_C_LOCALE(pszLocale)
991 || IS_POSIX_LOCALE(pszLocale))
992 {
993#define COPY(m) do { pLconv->m = __libc_gLocaleLconvDefault.m; } while (0)
994 COPY(s.int_curr_symbol);
995 COPY(s.currency_symbol);
996 COPY(s.mon_decimal_point);
997 COPY(s.mon_thousands_sep);
998 COPY(s.positive_sign);
999 COPY(s.negative_sign);
1000 COPY(pszCrncyStr);
1001 COPY(s.mon_grouping);
1002 COPY(s.int_frac_digits);
1003 COPY(s.frac_digits);
1004 COPY(s.p_cs_precedes);
1005 COPY(s.p_sep_by_space);
1006 COPY(s.n_cs_precedes);
1007 COPY(s.n_sep_by_space);
1008 COPY(s.p_sign_posn);
1009 COPY(s.n_sign_posn);
1010 COPY(s.int_p_cs_precedes);
1011 COPY(s.int_n_cs_precedes);
1012 COPY(s.int_p_sep_by_space);
1013 COPY(s.int_n_sep_by_space);
1014 COPY(s.int_p_sign_posn);
1015 COPY(s.int_n_sign_posn);
1016 pLconv->fMonetaryConsts = 1;
1017#undef COPY
1018 return 0;
1019 }
1020 /*
1021 * Convert the stuff.
1022 */
1023#define FIXMAX(val) \
1024 ( (val) != 0xff ? (val) : CHAR_MAX ) /* (assumes CHAR_MAX == 0x7f) */
1025#define CONVERT_UCS(field) \
1026 do { rc = convert_ucs(uobj, pULconv->field, &pLconv->s.field); if (rc) return rc; } while (0)
1027
1028 if (pszModifier && IS_EURO(pszModifier))
1029 {
1030 /** @todo check for specs on a standard EURO grouping and stuff. */
1031 pLconv->s.currency_symbol = strdup("EUR");
1032 pLconv->s.int_curr_symbol = strdup("EUR");
1033 }
1034 else
1035 {
1036 CONVERT_UCS(int_curr_symbol);
1037 CONVERT_UCS(currency_symbol);
1038 }
1039 CONVERT_UCS(mon_decimal_point);
1040 CONVERT_UCS(mon_thousands_sep);
1041 CONVERT_UCS(positive_sign);
1042 CONVERT_UCS(negative_sign);
1043
1044 pLconv->s.int_frac_digits = FIXMAX(pULconv->int_frac_digits);
1045 pLconv->s.frac_digits = FIXMAX(pULconv->frac_digits);
1046 pLconv->s.p_cs_precedes = FIXMAX(pULconv->p_cs_precedes);
1047 pLconv->s.p_sep_by_space = FIXMAX(pULconv->p_sep_by_space);
1048 pLconv->s.n_cs_precedes = FIXMAX(pULconv->n_cs_precedes);
1049 pLconv->s.n_sep_by_space = FIXMAX(pULconv->n_sep_by_space);
1050 pLconv->s.p_sign_posn = FIXMAX(pULconv->p_sign_posn);
1051 pLconv->s.n_sign_posn = FIXMAX(pULconv->n_sign_posn);
1052 /* we fake the international variants here. */
1053 pLconv->s.int_p_cs_precedes = pLconv->s.p_cs_precedes;
1054 pLconv->s.int_n_cs_precedes = pLconv->s.n_cs_precedes;
1055 pLconv->s.int_p_sep_by_space= pLconv->s.p_sep_by_space;
1056 pLconv->s.int_n_sep_by_space= pLconv->s.n_sep_by_space;
1057 pLconv->s.int_p_sign_posn = pLconv->s.p_sign_posn;
1058 pLconv->s.int_n_sign_posn = pLconv->s.n_sign_posn;
1059
1060#undef FIXMAX
1061#undef CONVERT_UCS
1062
1063 /*
1064 * Extra stuff (which I don't know which member corresponds to).
1065 */
1066 if ((rc = query_item(lobj, uobj, CRNCYSTR, &pLconv->pszCrncyStr)))
1067 return rc;
1068
1069 return localeConvertGrouping(pULconv->mon_grouping, &pLconv->s.mon_grouping);
1070}
1071
1072/**
1073 * Frees all heap strings in the monetary part of the lconv structure.
1074 * @param pLconv What to work on.
1075 */
1076static void localeMessagesFree(__LIBC_PLOCALEMSG pMsg)
1077{
1078 if (!pMsg->fConsts)
1079 {
1080#define FREE(x) do { if (pMsg->x) free(pMsg->x); pMsg->x = NULL; } while (0)
1081 FREE(pszYesExpr);
1082 FREE(pszNoExpr);
1083 FREE(pszYesStr);
1084 FREE(pszNoStr);
1085#undef FREE
1086 }
1087 pMsg->fConsts = 0;
1088}
1089
1090
1091/**
1092 * Sets the LC_MESSAGES part of the locale.
1093 *
1094 * @returns 0 on success.
1095 * @returns negated errno on failure.
1096 * @param pMsg The messages locale info structure to operate on.
1097 * @param uobj The UconvObject to use.
1098 * @param lobj The LocaleObject to use.
1099 * @param pszLocale Pointer to the locale base specifier.
1100 */
1101static int localeMessagesDo(__LIBC_PLOCALEMSG pMsg, UconvObject uobj, LocaleObject lobj, const char *pszLocale)
1102{
1103 int rc;
1104 /* free any old stuff. */
1105 localeMessagesFree(pMsg);
1106
1107 /*
1108 * For "C" / "POSIX" we can simply use the static default locale.
1109 */
1110 if ( IS_C_LOCALE(pszLocale)
1111 || IS_POSIX_LOCALE(pszLocale))
1112 {
1113 memcpy(pMsg, &__libc_gLocaleMsgDefault, sizeof(*pMsg));
1114 return 0;
1115 }
1116
1117 /* query the items. */
1118 if ( (rc = query_item(lobj, uobj, YESEXPR, &pMsg->pszYesExpr))
1119 || (rc = query_item(lobj, uobj, NOEXPR, &pMsg->pszNoExpr))
1120 || (rc = query_item(lobj, uobj, YESSTR, &pMsg->pszYesStr))
1121 || (rc = query_item(lobj, uobj, NOSTR, &pMsg->pszNoStr))
1122 )
1123 {
1124 return rc;
1125 }
1126
1127 return 0;
1128}
1129
1130
1131/**
1132 * Free an entry in the global locale data array.
1133 */
1134static void localeGlobalFree(__LIBC_PLOCALEGLOBAL pGlobal, int iCategory)
1135{
1136 if (pGlobal->apszNames[iCategory + 1])
1137 {
1138 if ( pGlobal->apszNames[iCategory + 1] != gszC
1139 && pGlobal->apszNames[iCategory + 1] != gszPOSIX)
1140 free(pGlobal->apszNames[iCategory + 1]);
1141 pGlobal->apszNames[iCategory + 1] = NULL;
1142 }
1143}
1144
1145
1146/**
1147 * Parses out the locale spec and the code page spec.
1148 */
1149static int localeParseLocale(char *pszLocale, const char **ppszCodepage, const char **ppszModifier)
1150{
1151 /*
1152 * Modifier.
1153 */
1154 char *psz = strchr(pszLocale, '@');
1155 if (psz)
1156 {
1157 *psz++ = '\0';
1158 LIBCLOG_MSG2("Using locale modifier '%s'\n", psz);
1159 }
1160 *ppszModifier = psz;
1161
1162 /*
1163 * Codepage.
1164 */
1165 psz = strchr(pszLocale, '.');
1166 if (psz)
1167 *psz++ = '\0';
1168 *ppszCodepage = psz;
1169
1170 return 0;
1171}
1172
1173/**
1174 * Get the default local specification.
1175 * @returns Pointer to default local (can be environment or it could be pszBuffer).
1176 * @param pszBuffer Where to store the default local.
1177 */
1178static const char *getDefaultLocale(const char *pszCategory, char *pszBuffer, int *pfDefault)
1179{
1180 /* Copy pszLocale to a local storage since we'll modify it during parsing.
1181 If pszLocale value is a empty string, user wants the defaults fetched from
1182 environment. */
1183 const char *pszRet = getenv("LC_ALL");
1184 if (pszRet && *pszRet)
1185 *pfDefault = 0; /* LC_ALL is not default, it's an override of everything else. */
1186 else
1187 {
1188 *pfDefault = 1;
1189 pszRet = getenv(pszCategory);
1190 if (!pszRet)
1191 {
1192 pszRet = getenv("LANG");
1193 if (!pszRet)
1194 {
1195 /*
1196 * The default is 'C' or 'POSIX'.
1197 *
1198 * But if old style is enabled we'll be using the country
1199 * info to get a locale.
1200 */
1201 pszRet = gszC;
1202 if (gfOldStyle < 0)
1203 gfOldStyle = getenv("LIBC_SETLOCALE_OLDSTYLE") != NULL;
1204 if (gfOldStyle)
1205 {
1206 /*
1207 * Not specified nor in environment, use country info.
1208 * This is actually wrong in POSIX sense, but it makes "OS/2 sense". :)
1209 */
1210 COUNTRYCODE ctryc = {0,0};
1211 COUNTRYINFO ctryi = {0};
1212 ULONG cb;
1213 int rc;
1214 FS_VAR()
1215
1216 FS_SAVE_LOAD();
1217 rc = DosQueryCtryInfo(sizeof(ctryi), &ctryc, &ctryi, &cb);
1218 if (!rc /*|| rc == ERROR_COU*/)
1219 {
1220 UniChar ucs[50];
1221 rc = UniMapCtryToLocale(ctryi.country, ucs, sizeof(ucs));
1222 if (!rc)
1223 {
1224 Ucs2Sb(ucs, pszBuffer, UniStrlen(ucs) + 1);
1225 pszRet = pszBuffer;
1226 }
1227 else
1228 LIBC_ASSERTM_FAILED("UniMapCtryToLocale(%ld) failed rc=%d\n", ctryi.country, rc);
1229 }
1230 else
1231 LIBC_ASSERTM_FAILED("DosQueryCtryInfo failed rc=%d\n", rc);
1232 FS_RESTORE();
1233 }
1234 }
1235 }
1236 }
1237 return pszRet;
1238}
1239
1240/**
1241 * Extracts the code page from the locale spec or gets the default
1242 * code page.
1243 * @returns 0 on success.
1244 * @returns negated errno on failure.
1245 * @param pszCodepage Pointer to where the codepage specifier starts.
1246 * @param pszLocale Pointer to the locale base specifier.
1247 * @param lobj The locale object handle.
1248 * @param pucsCodepage Where to store the code page.
1249 * @param cucCodepage Number of UniChar's in the buffer.
1250 */
1251static int getCodepage(const char *pszCodepage, const char *pszLocale, LocaleObject lobj, UniChar *pucsCodepage, unsigned cucCodepage)
1252{
1253 /*
1254 * Look at what the user provides.
1255 */
1256 if (pszCodepage && *pszCodepage)
1257 __libc_TranslateCodepage(pszCodepage, pucsCodepage);
1258 else
1259 {
1260#if 0 /* See ticket #227, #240, #187 and probably more. */
1261 int rc = -1;
1262 if (gfOldStyle < 0)
1263 gfOldStyle = getenv("LIBC_SETLOCALE_OLDSTYLE") != NULL;
1264
1265 /*
1266 * The locale object contains codepage information.
1267 * We'll use that unless someone want's the old style.
1268 */
1269 if (!gfOldStyle)
1270 {
1271 UniChar *pucsItem;
1272 rc = UniQueryLocaleItem(lobj, LOCI_sISOCodepage, &pucsItem);
1273 if (!rc)
1274 {
1275 UniChar *pucs = pucsItem;
1276 while ( (*pucsCodepage++ = *pucs++) != '\0')
1277 /* nada */;
1278 UniFreeMem(pucsItem);
1279 return 0;
1280 }
1281 }
1282
1283 /*
1284 * Old style / fallback.
1285 */
1286#endif
1287 if (IS_C_LOCALE(pszLocale) || IS_POSIX_LOCALE(pszLocale))
1288 /*
1289 * The "C" character encoding maps to ISO8859-1 which is not quite true,
1290 * but Unicode API doesn't have a codepage that matches the POSIX "C"
1291 * pszCodepage, so that's what we presume when user requests the "C" pszCodepage.
1292 */
1293 memcpy(pucsCodepage, gucsISO8859_1, sizeof(gucsISO8859_1));
1294 else
1295 {
1296 /*
1297 * Consider current process codepage as default for specified language.
1298 */
1299 ULONG aulCPs[5];
1300 ULONG cb;
1301 int rc;
1302 FS_VAR()
1303
1304 FS_SAVE_LOAD();
1305 rc = DosQueryCp(sizeof(aulCPs), &aulCPs[0], &cb);
1306 if (rc)
1307 {
1308 FS_RESTORE();
1309 LIBC_ASSERTM_FAILED("DosQueryCp failed with rc=%d\n", rc);
1310 return -EDOOFUS;
1311 }
1312 LIBC_ASSERT(cb >= sizeof(ULONG));
1313 LIBCLOG_MSG2("locale: using process codepage %ld\n", aulCPs[0]);
1314 rc = UniMapCpToUcsCp(aulCPs[0], pucsCodepage, cucCodepage);
1315 FS_RESTORE();
1316 if (rc)
1317 {
1318 LIBC_ASSERTM_FAILED("UniMapCpToUcsCp(%ld,,) -> %d\n", aulCPs[0], rc);
1319 return -unierr2errno(rc);
1320 }
1321 }
1322 }
1323 return 0;
1324}
1325
1326
1327/**
1328 * Creates the libuni objects we need.
1329 */
1330int __libc_localeCreateObjects(const char *pszLocale, const char *pszCodepage, char *pszCodepageActual, LocaleObject *plobj, UconvObject *puobj)
1331{
1332 LIBCLOG_ENTER("pszLocale=%p:{%s} pszCodepage=%p:{%s} pszCodepageActual=%p plobj=%p puobj=%p\n",
1333 pszLocale, pszLocale, pszCodepage, pszCodepage, pszCodepageActual, (void *)plobj, (void *)puobj);
1334 UniChar ucsCodepage[CODEPAGE_MAX_LENGTH];
1335 int rc;
1336
1337 /*
1338 * Create locale object.
1339 */
1340 if (IS_C_LOCALE(pszLocale) || IS_POSIX_LOCALE(pszLocale))
1341 rc = UniCreateLocaleObject(UNI_MBS_STRING_POINTER, gszC, plobj);
1342 else
1343 rc = UniCreateLocaleObject(UNI_MBS_STRING_POINTER, pszLocale, plobj);
1344 if (rc)
1345 {
1346 LIBCLOG_ERROR("UniCreateLocaleObject(,%p:{%s},) -> rc=%d\n", pszLocale, pszLocale, rc);
1347 rc = -unierr2errno(rc);
1348 LIBCLOG_ERROR_RETURN_INT(rc);
1349 }
1350
1351 /*
1352 * Calc code page and create object.
1353 */
1354 rc = getCodepage(pszCodepage, pszLocale, *plobj, &ucsCodepage[0], sizeof(ucsCodepage) / sizeof(ucsCodepage[0]));
1355 if (!rc)
1356 {
1357 rc = UniCreateUconvObject(ucsCodepage, puobj);
1358 if (!rc)
1359 {
1360 if (pszCodepageActual)
1361 {
1362 size_t cchCodepageActual = UniStrlen(ucsCodepage);
1363 Ucs2Sb(ucsCodepage, pszCodepageActual, cchCodepageActual + 1);
1364
1365 /*
1366 * For some common codeset specs we'll normalize the naming.
1367 */
1368 const char *psz;
1369 if ((psz = strstr(pszCodepageActual,
1370 "ibm-1208!" "Ibm-1208!" "IBm-1208!" "IBM-1208!" "IbM-1208!" "iBm-1208!" "iBM-1208!" "ibM-1208!"
1371 "utf-8!" "Utf-8!" "UTf-8!" "UTF-8!" "UtF-8!" "uTf-8!" "uTF-8!" "utF-8!"
1372 "utf8!" "Utf8!" "UTf8!" "UTF8!" "UtF8!" "uTf8!" "uTF8!" "utF8!")) != NULL
1373 && psz[cchCodepageActual] == '!')
1374 memcpy(pszCodepageActual, "UTF-8", sizeof("UTF-8"));
1375 else if ((psz = strstr(pszCodepageActual,
1376 "ibm-1200!" "Ibm-1200!" "IBm-1200!" "IBM-1200!" "IbM-1200!" "iBm-1200!" "iBM-1200!" "ibM-1200!"
1377 "ucs-2!" "Ucs-2!" "UCs-2!" "UCS-2!" "UcS-2!" "uCs-2!" "uCS-2!" "ucS-2!"
1378 "ucs2!" "Ucs2!" "UCs2!" "UCS2!" "UcS2!" "uCs2!" "uCS2!" "ucS2!")) != NULL
1379 && psz[cchCodepageActual] == '!')
1380 memcpy(pszCodepageActual, "UCS-2", sizeof("UCS-2"));
1381 }
1382 LIBCLOG_RETURN_MSG(rc, "ret 0 *plobj=%08x *puobj=%08x pszCodepageActual=%p:{%s}\n",
1383 (unsigned)*plobj, (unsigned)*puobj, pszCodepageActual, pszCodepageActual);
1384 }
1385
1386 LIBCLOG_ERROR("UniCreateUconvObject(%ls,) -> rc=%d\n", (wchar_t *)ucsCodepage, rc);
1387 rc = -unierr2errno(rc);
1388 }
1389
1390 UniFreeLocaleObject(*plobj);
1391 LIBCLOG_ERROR_RETURN_INT(rc);
1392}
1393
1394
1395
1396/**
1397 * Perform the locale operation on one category.
1398 */
1399static int localeDoOne(struct temp_locale *pTemp, int iCategory, const char *pszLocale, const char *pszCodepage, const char *pszModifier)
1400{
1401 LIBCLOG_ENTER("pTemp=%p iCategory=%d (%s) pszLocale=%p:{%s} pszCodepage=%p:{%s} pszModifier=%p:{%s}\n",
1402 (void *)pTemp, iCategory, gaszCategories[iCategory + 1], pszLocale, pszLocale, pszCodepage, pszCodepage, pszModifier, pszModifier);
1403 char szCodepageActual[CODEPAGE_MAX_LENGTH];
1404 UconvObject uobj;
1405 LocaleObject lobj;
1406 int rc;
1407 int fFree;
1408
1409
1410 /*
1411 * Create the objects.
1412 */
1413 rc = __libc_localeCreateObjects(pszLocale, pszCodepage, &szCodepageActual[0], &lobj, &uobj);
1414 if (rc)
1415 return rc;
1416
1417 /*
1418 * Call the worker for the locale category.
1419 */
1420 fFree = 1;
1421 pTemp->afProcessed[iCategory + 1] = 1;
1422 switch (iCategory)
1423 {
1424 case LC_COLLATE:
1425 rc = localeCollateDo(&pTemp->Collate, uobj, lobj, pszLocale);
1426 fFree = rc != 0;
1427 break;
1428
1429 case LC_CTYPE:
1430 rc = localeCtypeDo(&pTemp->Ctype, uobj, lobj, pszLocale, &szCodepageActual[0]);
1431 fFree = rc != 0 || IS_C_LOCALE(pszLocale) || IS_POSIX_LOCALE(pszLocale);
1432 break;
1433
1434 case LC_TIME:
1435 rc = localeTimeDo(&pTemp->Time, uobj, lobj, pszLocale);
1436 break;
1437
1438 case LC_NUMERIC:
1439 case LC_MONETARY:
1440 {
1441 /*
1442 * Query the unicode data..
1443 */
1444 struct UniLconv *pULconv;
1445 rc = UniQueryLocaleInfo(lobj, &pULconv);
1446 if (!rc)
1447 {
1448 if (iCategory == LC_NUMERIC)
1449 rc = localeNumericDo(&pTemp->Lconv, uobj, pULconv, pszLocale);
1450 else
1451 rc = localeMonetaryDo(&pTemp->Lconv, uobj, lobj, pULconv, pszLocale, pszModifier);
1452 UniFreeLocaleInfo(pULconv);
1453 }
1454 else
1455 {
1456 LIBCLOG_ERROR("UniQueryLocaleInfo -> %d\n", rc);
1457 rc = -unierr2errno(rc);
1458 }
1459 break;
1460 }
1461
1462 case LC_MESSAGES:
1463 rc = localeMessagesDo(&pTemp->Msg, uobj, lobj, pszLocale);
1464 break;
1465
1466 default:
1467 rc = 0;
1468 break;
1469 }
1470
1471
1472 /*
1473 * Cleanup.
1474 */
1475 if (fFree)
1476 {
1477 UniFreeLocaleObject(lobj);
1478 UniFreeUconvObject(uobj);
1479 }
1480
1481 if (!rc)
1482 {
1483 /*
1484 * Build and set catagory value.
1485 * The 'pszLocale' variable already contains language and country.
1486 */
1487 localeGlobalFree(&pTemp->Global, iCategory);
1488 if ((!pszCodepage || !*pszCodepage) && IS_C_LOCALE(pszLocale))
1489 pTemp->Global.apszNames[iCategory + 1] = (char *)gszC;
1490 else if ((!pszCodepage || !*pszCodepage) && IS_POSIX_LOCALE(pszLocale))
1491 pTemp->Global.apszNames[iCategory + 1] = (char *)gszPOSIX;
1492 else
1493 {
1494 /* pszLocale + "." + szCodepageActual [+ "@" + pszModifier] */
1495 int cch1 = strlen(pszLocale);
1496 int cch2 = strlen(szCodepageActual);
1497 int cch3 = pszModifier ? strlen(pszModifier) + 1 : 0;
1498 char *psz = pTemp->Global.apszNames[iCategory + 1] = malloc(cch1 + cch2 + cch3 + 2);
1499 if (!psz)
1500 return -ENOMEM;
1501 memcpy(psz, pszLocale, cch1);
1502 psz += cch1;
1503
1504 *psz++ = '.';
1505 memcpy(psz, szCodepageActual, cch2);
1506 psz += cch2;
1507 *psz = '\0';
1508
1509 if (cch3)
1510 {
1511 *psz++ = '@';
1512 memcpy(psz, pszModifier, cch3);
1513 }
1514
1515 LIBCLOG_MSG2("Setting iCategory='%d''%s' locale value to '%s'\n",
1516 iCategory, gaszCategories[iCategory + 1], pTemp->Global.apszNames[iCategory + 1]);
1517 }
1518 }
1519
1520 LIBCLOG_RETURN_INT(rc);
1521}
1522
1523
1524
1525/**
1526 * Perform the more complex setlocale() operations which requires that
1527 * failure doesn't change anything. It will use an auto variable for
1528 * the temporary locale structure comitting it if all goes fine.
1529 *
1530 * @returns 0 on success.
1531 * @returns Negative errno on failure.
1532 */
1533static int localeDo(struct temp_locale *pTemp, int iCategory, char *pszLocale, int fDefaultValue)
1534{
1535 LIBCLOG_ENTER("pTemp=%p iCategory=%d (%s) pszLocale=%p:{%s} fDefaultValue=%d\n",
1536 (void *)pTemp, iCategory, gaszCategories[iCategory + 1], pszLocale, pszLocale, fDefaultValue);
1537 const char *pszCodepage;
1538 const char *pszModifier;
1539 char *pszNext;
1540 int rc;
1541
1542 /*
1543 * Process it.
1544 */
1545 if (iCategory != LC_ALL)
1546 {
1547 rc = localeParseLocale(pszLocale, &pszCodepage, &pszModifier);
1548 if (!rc)
1549 rc = localeDoOne(pTemp, iCategory, pszLocale, pszCodepage, pszModifier);
1550 }
1551 else
1552 {
1553 /*
1554 * Parse the pszLocale string user passed to us. This is either a string
1555 * in the XPG format (see below) or a list of values of the
1556 * form "CATEGORY1=value1;CATEGORY2=value2[;...]", where values are
1557 * also in XPG format: "language[_territory[.codeset]][@modifier]".
1558 * Currently we're only handling the @EURO/@euro modifiers and ignoring the rest.
1559 */
1560 pszNext = strchr(pszLocale, ';');
1561 if (pszNext)
1562 {
1563 /*
1564 * User supplied a list of iCategory=value statements separated with ';'.
1565 */
1566 char *pszCur = pszLocale;
1567 *pszNext++ = '\0'; /* remove the ';'. */
1568 for (;;)
1569 {
1570 int iCat;
1571 /* Search for the variable, ignoring those we cannot find.s */
1572 for (rc = 0, iCat = LC_ALL; iCat < _LC_LAST; iCat++)
1573 {
1574 unsigned cch = gacchCategories[iCat + 1];
1575 if ( strncmp(pszCur, gaszCategories[iCat + 1], cch) == 0
1576 && pszCur[cch] == '=')
1577 {
1578 char *pszVal = &pszCur[cch + 1];
1579 const char *pszValCp;
1580 const char *pszValQual;
1581 /* parse the locale value. */
1582 rc = localeParseLocale(pszVal, &pszValCp, &pszValQual);
1583 if (!rc)
1584 {
1585 if (iCat != LC_ALL)
1586 rc = localeDoOne(pTemp, iCat, pszVal, pszValCp, pszValQual);
1587 else /* Iterate all categories except LC_ALL. */
1588 for (iCat = LC_ALL + 1; !rc && iCat < _LC_LAST; iCat++)
1589 rc = localeDoOne(pTemp, iCat, pszVal, pszValCp, pszValQual);
1590 }
1591 break;
1592 }
1593 }
1594
1595 /* next */
1596 if (!pszNext || rc < 0)
1597 break;
1598 pszCur = pszNext;
1599 pszNext = strchr(pszCur, ';');
1600 if (pszNext)
1601 *pszNext++ = '\0';
1602 }
1603 }
1604 else
1605 {
1606 /*
1607 * Set all pszLocale categories to given value.
1608 * Parse it first to save time.
1609 */
1610 rc = localeParseLocale(pszLocale, &pszCodepage, &pszModifier);
1611 if (!rc)
1612 {
1613 int iCat;
1614 for (iCat = LC_ALL + 1; !rc && iCat < _LC_LAST; iCat++)
1615 {
1616 const char *pszEnv;
1617 /*
1618 * If user wants default values, we must check environment first.
1619 */
1620 if (fDefaultValue && (pszEnv = getenv(gaszCategories[iCat + 1])) != NULL)
1621 {
1622 const char *pszCodepageEnv;
1623 const char *pszModifierEnv;
1624 char *pszCopy = alloca(strlen(pszEnv) + 1);
1625 if (!pszCopy)
1626 LIBCLOG_RETURN_INT(-ENOMEM);
1627 rc = localeParseLocale(strcpy(pszCopy, pszEnv), &pszCodepageEnv, &pszModifierEnv);
1628 if (!rc)
1629 rc = localeDoOne(pTemp, iCat, pszCopy, pszCodepageEnv, pszModifierEnv);
1630 }
1631 else
1632 rc = localeDoOne(pTemp, iCat, pszLocale, pszCodepage, pszModifier);
1633 }
1634 }
1635 }
1636 }
1637 LIBCLOG_RETURN_INT(rc);
1638}
1639
1640
1641/**
1642 * Commits a temporary local and updates the global locale strings.
1643 */
1644static char *localeCommit(struct temp_locale *pTemp, int iCategory)
1645{
1646 char *pszRet;
1647 char *pszAll;
1648 int cch;
1649 int iCat;
1650
1651 /*
1652 * Lock the structure.
1653 */
1654 _smutex_request(&gLocale.lock);
1655
1656 /*
1657 * Copy all the data.
1658 */
1659 if (pTemp->afProcessed[LC_COLLATE + 1])
1660 {
1661 localeCollateFree(&__libc_gLocaleCollate);
1662 memcpy(&__libc_gLocaleCollate, &pTemp->Collate, sizeof(__libc_gLocaleCollate));
1663 pTemp->afProcessed[LC_COLLATE + 1] = 0;
1664 gLocale.apszNames[LC_COLLATE + 1] = pTemp->Global.apszNames[LC_COLLATE + 1];
1665 }
1666
1667 if (pTemp->afProcessed[LC_CTYPE + 1])
1668 {
1669 localeCtypeFree(&__libc_GLocaleCtype);
1670 memcpy(&__libc_GLocaleCtype, &pTemp->Ctype, sizeof(__libc_GLocaleCtype));
1671 MB_CUR_MAX = pTemp->Ctype.mbcs ? pTemp->Ctype.mbcs : 1;
1672 if ( IS_C_LOCALE(pTemp->Global.apszNames[LC_CTYPE + 1])
1673 || IS_POSIX_LOCALE(pTemp->Global.apszNames[LC_CTYPE + 1]))
1674 __libc_GLocaleWCtype.uMask = ~0x7fU;
1675 else
1676 __libc_GLocaleWCtype.uMask = ~0xffU;
1677 pTemp->afProcessed[LC_CTYPE + 1] = 0;
1678 gLocale.apszNames[LC_CTYPE + 1] = pTemp->Global.apszNames[LC_CTYPE + 1];
1679 }
1680
1681 if (pTemp->afProcessed[LC_TIME + 1])
1682 {
1683 localeTimeFree(&__libc_gLocaleTime);
1684 memcpy(&__libc_gLocaleTime, &pTemp->Time, sizeof(__libc_gLocaleTime));
1685 pTemp->afProcessed[LC_TIME + 1] = 0;
1686 gLocale.apszNames[LC_TIME + 1] = pTemp->Global.apszNames[LC_TIME + 1];
1687 }
1688
1689 if (pTemp->afProcessed[LC_NUMERIC + 1])
1690 {
1691 localeNumericFree(&__libc_gLocaleLconv);
1692 __libc_gLocaleLconv.fNumericConsts = pTemp->Lconv.fNumericConsts;
1693 __libc_gLocaleLconv.s.decimal_point = pTemp->Lconv.s.decimal_point;
1694 __libc_gLocaleLconv.s.thousands_sep = pTemp->Lconv.s.thousands_sep;
1695 __libc_gLocaleLconv.s.grouping = pTemp->Lconv.s.grouping;
1696 pTemp->afProcessed[LC_NUMERIC + 1] = 0;
1697 gLocale.apszNames[LC_NUMERIC + 1] = pTemp->Global.apszNames[LC_NUMERIC + 1];
1698 }
1699
1700 if (pTemp->afProcessed[LC_MONETARY + 1])
1701 {
1702 localeMonetaryFree(&__libc_gLocaleLconv);
1703 __libc_gLocaleLconv.fMonetaryConsts = pTemp->Lconv.fMonetaryConsts;
1704 __libc_gLocaleLconv.s.int_curr_symbol = pTemp->Lconv.s.int_curr_symbol;
1705 __libc_gLocaleLconv.s.currency_symbol = pTemp->Lconv.s.currency_symbol;
1706 __libc_gLocaleLconv.s.mon_decimal_point = pTemp->Lconv.s.mon_decimal_point;
1707 __libc_gLocaleLconv.s.mon_thousands_sep = pTemp->Lconv.s.mon_thousands_sep;
1708 __libc_gLocaleLconv.s.mon_grouping = pTemp->Lconv.s.mon_grouping;
1709 __libc_gLocaleLconv.s.positive_sign = pTemp->Lconv.s.positive_sign;
1710 __libc_gLocaleLconv.s.negative_sign = pTemp->Lconv.s.negative_sign;
1711 __libc_gLocaleLconv.s.int_frac_digits = pTemp->Lconv.s.int_frac_digits;
1712 __libc_gLocaleLconv.s.frac_digits = pTemp->Lconv.s.frac_digits;
1713 __libc_gLocaleLconv.s.p_cs_precedes = pTemp->Lconv.s.p_cs_precedes;
1714 __libc_gLocaleLconv.s.p_sep_by_space = pTemp->Lconv.s.p_sep_by_space;
1715 __libc_gLocaleLconv.s.n_cs_precedes = pTemp->Lconv.s.n_cs_precedes;
1716 __libc_gLocaleLconv.s.n_sep_by_space = pTemp->Lconv.s.n_sep_by_space;
1717 __libc_gLocaleLconv.s.p_sign_posn = pTemp->Lconv.s.p_sign_posn;
1718 __libc_gLocaleLconv.s.n_sign_posn = pTemp->Lconv.s.n_sign_posn;
1719 __libc_gLocaleLconv.s.int_p_cs_precedes = pTemp->Lconv.s.int_p_cs_precedes;
1720 __libc_gLocaleLconv.s.int_n_cs_precedes = pTemp->Lconv.s.int_n_cs_precedes;
1721 __libc_gLocaleLconv.s.int_p_sep_by_space = pTemp->Lconv.s.int_p_sep_by_space;
1722 __libc_gLocaleLconv.s.int_n_sep_by_space = pTemp->Lconv.s.int_n_sep_by_space;
1723 __libc_gLocaleLconv.s.int_p_sign_posn = pTemp->Lconv.s.int_p_sign_posn;
1724 __libc_gLocaleLconv.s.int_n_sign_posn = pTemp->Lconv.s.int_n_sign_posn;
1725 __libc_gLocaleLconv.pszCrncyStr = pTemp->Lconv.pszCrncyStr;
1726 pTemp->afProcessed[LC_MONETARY + 1] = 0;
1727 gLocale.apszNames[LC_MONETARY + 1] = pTemp->Global.apszNames[LC_MONETARY + 1];
1728 }
1729 if (pTemp->afProcessed[LC_MESSAGES + 1])
1730 {
1731 localeMessagesFree(&__libc_gLocaleMsg);
1732 memcpy(&__libc_gLocaleMsg, &pTemp->Msg, sizeof(__libc_gLocaleMsg));
1733 pTemp->afProcessed[LC_MESSAGES + 1] = 0;
1734 gLocale.apszNames[LC_MESSAGES + 1] = pTemp->Global.apszNames[LC_MESSAGES + 1];
1735 }
1736
1737
1738 /*
1739 * Now we must build the LC_ALL string.
1740 *
1741 * If all the entries are not identical we must make a "category=value"
1742 * string for LC_ALL. Else we can just duplicate one of the others.
1743 */
1744 pszAll = gLocale.apszNames[1];
1745 cch = gacchCategories[1] + 1 + strlen(pszAll) + 1 + 1;
1746 for (iCat = 2; iCat <= _LC_LAST; iCat++)
1747 {
1748 int cchCat = strlen(gLocale.apszNames[iCat]);
1749 cch += gacchCategories[iCat] + 1 + cchCat + 1;
1750 if (pszAll && strcmp(pszAll, gLocale.apszNames[iCat]))
1751 pszAll = NULL;
1752 }
1753
1754 if (!pszAll)
1755 {
1756 /*
1757 * Not identical. Generate composite value.
1758 */
1759 char *psz = pszAll = malloc(cch); /* If we're out of memory here, then it's just too bad :-/ */
1760 for (iCat = 1; iCat <= _LC_LAST; iCat++) /* (iCat is array index not lc idx this time) */
1761 {
1762 int cchCat = gacchCategories[iCat];
1763 memcpy(psz, gaszCategories[iCat], cchCat);
1764 psz += cchCat;
1765 *psz++ = '=';
1766 cchCat = strlen(gLocale.apszNames[iCat]);
1767 memcpy(psz, gLocale.apszNames[iCat], cchCat);
1768 psz += cchCat;
1769 *psz++ = ';';
1770 }
1771 *psz = '\0';
1772 localeGlobalFree(&gLocale, LC_ALL);
1773 gLocale.apszNames[LC_ALL + 1] = pszAll;
1774 }
1775 else if (strcmp(gLocale.apszNames[LC_ALL + 1], pszAll))
1776 {
1777 localeGlobalFree(&gLocale, LC_ALL);
1778 gLocale.apszNames[LC_ALL + 1] = strdup(pszAll);
1779 }
1780
1781 /*
1782 * Unlock and returns.
1783 */
1784 pszRet = gLocale.apszNames[iCategory + 1];
1785 _smutex_release(&gLocale.lock);
1786
1787 return pszRet;
1788}
1789
1790
1791/**
1792 * Clean up the temporary locale instance structure.
1793 * @param pTemp Stuff to cleanup.
1794 */
1795static void localeFree(struct temp_locale *pTemp)
1796{
1797 int iCat;
1798 if (pTemp->afProcessed[LC_COLLATE + 1])
1799 localeCollateFree(&pTemp->Collate);
1800 if (pTemp->afProcessed[LC_CTYPE + 1])
1801 localeCtypeFree(&pTemp->Ctype);
1802 if (pTemp->afProcessed[LC_TIME + 1])
1803 localeTimeFree(&pTemp->Time);
1804 if (pTemp->afProcessed[LC_NUMERIC + 1])
1805 localeNumericFree(&pTemp->Lconv);
1806 if (pTemp->afProcessed[LC_MONETARY + 1])
1807 localeMonetaryFree(&pTemp->Lconv);
1808 if (pTemp->afProcessed[LC_MESSAGES + 1])
1809 localeMessagesFree(&pTemp->Msg);
1810 for (iCat = 0; iCat < _LC_LAST; iCat++)
1811 if (pTemp->afProcessed[iCat + 1])
1812 localeGlobalFree(&pTemp->Global, iCat);
1813}
1814
1815
1816/**
1817 * This setlocale() implementation differs from the specs in that any
1818 * options specified by '@...' other than EURO/euro is ignored.
1819 */
1820char *_STD(setlocale)(int iCategory, const char *pszLocale)
1821{
1822 LIBCLOG_ENTER("iCategory=%d pszLocale=%p:{%s}\n", iCategory, pszLocale, pszLocale);
1823 char szTmpBuf[64];
1824 int fDefaultValue;
1825 size_t cch;
1826 char *pszLocaleCopy;
1827 char *pszRet;
1828 int rc;
1829
1830 /*
1831 * Validate input.
1832 */
1833 if (iCategory < LC_ALL || iCategory >= _LC_LAST)
1834 {
1835 errno = EINVAL;
1836 LIBCLOG_ERROR_RETURN_MSG(NULL, "ret %p - iCategory=%d is invalid!\n", (void *)NULL, iCategory);
1837 }
1838
1839 /*
1840 * Check if user just queries current pszLocale.
1841 */
1842 if (!pszLocale)
1843 {
1844 pszRet = gLocale.apszNames[iCategory + 1];
1845 LIBCLOG_RETURN_MSG(pszRet, "ret %p:{%s}\n", pszRet, pszRet);
1846 }
1847
1848 /*
1849 * Check if user wants we to do the same job twice.
1850 */
1851 if (strcmp(pszLocale, gLocale.apszNames[iCategory + 1]) == 0)
1852 {
1853 /* We have to return the value of LC_ALL */
1854 pszRet = gLocale.apszNames[iCategory + 1];
1855 LIBCLOG_RETURN_MSG(pszRet, "ret %p:{%s} (already set)\n", pszRet, pszRet);
1856 }
1857
1858
1859 /*
1860 * If pszLocale value is a empty string, user wants the defaults fetched from
1861 * environment.
1862 */
1863 fDefaultValue = *pszLocale == '\0';
1864 if (fDefaultValue)
1865 pszLocale = getDefaultLocale(gaszCategories[iCategory + 1], szTmpBuf, &fDefaultValue);
1866
1867 /*
1868 * Copy pszLocale to a local storage since we'll modify it during parsing.
1869 */
1870 cch = strlen(pszLocale) + 1;
1871 pszLocaleCopy = (char *)alloca(cch);
1872 if (!pszLocaleCopy)
1873 {
1874 errno = ENOMEM;
1875 LIBCLOG_ERROR_RETURN_P(NULL);
1876 }
1877 memcpy(pszLocaleCopy, pszLocale, cch);
1878
1879
1880 /*
1881 * Allocate a temporary locale state and perform
1882 * the locale operation on that.
1883 */
1884 struct temp_locale *pTemp = alloca(sizeof(struct temp_locale));
1885 bzero(pTemp, sizeof(struct temp_locale));
1886
1887 rc = localeDo(pTemp, iCategory, pszLocaleCopy, fDefaultValue);
1888
1889 /*
1890 * If successful commit the temporary locale.
1891 */
1892 if (!rc)
1893 pszRet = localeCommit(pTemp, iCategory);
1894 else
1895 {
1896 errno = -rc;
1897 pszRet = NULL;
1898 }
1899
1900 /*
1901 * Cleanup and exit.
1902 */
1903 localeFree(pTemp);
1904
1905 if (pszRet)
1906 LIBCLOG_RETURN_MSG(pszRet, "ret %p:{%s}\n", pszRet, pszRet);
1907 LIBCLOG_ERROR_RETURN_P(NULL);
1908}
1909
1910#undef ERROR
1911
1912#undef __LIBC_LOG_GROUP
1913#define __LIBC_LOG_GROUP __LIBC_LOG_GRP_FORK
1914
1915
1916_FORK_CHILD1(0xffffff00, setlocalForkChild1)
1917
1918/**
1919 * Create any unicode objects used by the locale stuff.
1920 *
1921 * !describe me!
1922 */
1923static int setlocalForkChild1(__LIBC_PFORKHANDLE pForkHandle, __LIBC_FORKOP enmOperation)
1924{
1925 LIBCLOG_ENTER("pForkHandle=%p enmOperation=%d\n", (void *)pForkHandle, enmOperation);
1926 int rc;
1927 switch (enmOperation)
1928 {
1929 case __LIBC_FORK_OP_FORK_CHILD:
1930 {
1931 gLocale.lock = 0;
1932
1933 rc = 0;
1934 if ( __libc_GLocaleCtype.lobj
1935 || __libc_GLocaleCtype.uobj
1936 || __libc_gLocaleCollate.lobj
1937 || __libc_gLocaleCollate.uobj
1938 )
1939 {
1940 LocaleObject lobj;
1941 UconvObject uobj;
1942 const char *pszCodepage;
1943 const char *pszModifier;
1944 int cch1 = strlen(gLocale.apszNames[LC_CTYPE + 1]) + 1;
1945 int cch2 = strlen(gLocale.apszNames[LC_COLLATE + 1]) + 1;
1946 int cch = cch1 > cch2 ? cch1 : cch2;
1947 char *psz = alloca(cch);
1948 if (!psz)
1949 LIBCLOG_RETURN_INT(-ENOMEM);
1950
1951 if ( __libc_GLocaleCtype.lobj
1952 || __libc_GLocaleCtype.uobj)
1953 {
1954 memcpy(psz, gLocale.apszNames[LC_CTYPE + 1], cch);
1955 localeParseLocale(psz, &pszCodepage, &pszModifier);
1956 rc = __libc_localeCreateObjects(psz, pszCodepage, NULL, &lobj, &uobj);
1957 if (rc)
1958 LIBCLOG_RETURN_INT(rc);
1959
1960 if (__libc_GLocaleCtype.lobj)
1961 __libc_GLocaleCtype.lobj = lobj;
1962 else
1963 UniFreeLocaleObject(lobj);
1964
1965 if (__libc_GLocaleCtype.uobj)
1966 __libc_GLocaleCtype.uobj = uobj;
1967 else
1968 UniFreeUconvObject(uobj);
1969 }
1970
1971 if ( __libc_gLocaleCollate.lobj
1972 || __libc_gLocaleCollate.uobj)
1973 {
1974 memcpy(psz, gLocale.apszNames[LC_COLLATE + 1], cch);
1975 localeParseLocale(psz, &pszCodepage, &pszModifier);
1976 rc = __libc_localeCreateObjects(psz, pszCodepage, NULL, &lobj, &uobj);
1977 if (rc)
1978 LIBCLOG_RETURN_INT(rc);
1979
1980 if (__libc_gLocaleCollate.lobj)
1981 __libc_gLocaleCollate.lobj = lobj;
1982 else
1983 UniFreeLocaleObject(lobj);
1984
1985 if (__libc_gLocaleCollate.uobj)
1986 __libc_gLocaleCollate.uobj = uobj;
1987 else
1988 UniFreeUconvObject(uobj);
1989 }
1990 }
1991 break;
1992 }
1993 default:
1994 rc = 0;
1995 break;
1996 }
1997 LIBCLOG_RETURN_INT(rc);
1998}
1999
Note: See TracBrowser for help on using the repository browser.