1/***********************************************************************
2*                                                                      *
3*               This software is part of the ast package               *
4*          Copyright (c) 1985-2011 AT&T Intellectual Property          *
5*                      and is licensed under the                       *
6*                  Common Public License, Version 1.0                  *
7*                    by AT&T Intellectual Property                     *
8*                                                                      *
9*                A copy of the License is available at                 *
10*            http://www.opensource.org/licenses/cpl1.0.txt             *
11*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*                                                                      *
13*              Information and Software Systems Research               *
14*                            AT&T Research                             *
15*                           Florham Park NJ                            *
16*                                                                      *
17*                 Glenn Fowler <gsf@research.att.com>                  *
18*                  David Korn <dgk@research.att.com>                   *
19*                   Phong Vo <kpv@research.att.com>                    *
20*                                                                      *
21***********************************************************************/
22#pragma prototyped
23
24/*
25 * locale state implementation
26 */
27
28#include "lclib.h"
29#include "lclang.h"
30#include "FEATURE/locale"
31
32#include <ctype.h>
33
34typedef struct Local_s
35{
36	const char*	name;
37	int		size;
38} Local_t;
39
40#undef	setlocale	/* this file deals with the system locale */
41
42static Lc_numeric_t	default_numeric = { '.', -1 };
43
44static Lc_t		default_lc =
45{
46	"C",
47	"POSIX",
48	&lc_languages[0],
49	&lc_territories[0],
50	&lc_charsets[0],
51	0,
52	LC_default|LC_checked|LC_local,
53	0,
54	{
55		{ &default_lc, 0, 0 },
56		{ &default_lc, 0, 0 },
57		{ &default_lc, 0, 0 },
58		{ &default_lc, 0, 0 },
59		{ &default_lc, 0, 0 },
60		{ &default_lc, 0, (void*)&default_numeric },
61		{ &default_lc, 0, 0 },
62		{ &default_lc, 0, 0 },
63		{ &default_lc, 0, 0 },
64		{ &default_lc, 0, 0 },
65		{ &default_lc, 0, 0 },
66		{ &default_lc, 0, 0 },
67		{ &default_lc, 0, 0 },
68		{ &default_lc, 0, 0 }
69	}
70};
71
72static Lc_numeric_t	debug_numeric = { ',', '.' };
73
74static Lc_t		debug_lc =
75{
76	"debug",
77	"debug",
78	&lc_languages[1],
79	&lc_territories[1],
80	&lc_charsets[0],
81	0,
82	LC_debug|LC_checked|LC_local,
83	0,
84	{
85		{ &debug_lc, 0, 0 },
86		{ &debug_lc, 0, 0 },
87		{ &debug_lc, 0, 0 },
88		{ &debug_lc, 0, 0 },
89		{ &debug_lc, 0, 0 },
90		{ &debug_lc, 0, (void*)&debug_numeric },
91		{ &debug_lc, 0, 0 },
92		{ &debug_lc, 0, 0 },
93		{ &debug_lc, 0, 0 },
94		{ &debug_lc, 0, 0 },
95		{ &debug_lc, 0, 0 },
96		{ &debug_lc, 0, 0 },
97		{ &debug_lc, 0, 0 },
98		{ &debug_lc, 0, 0 }
99	},
100	&default_lc
101};
102
103static Lc_t*		lcs = &debug_lc;
104
105Lc_t*			locales[] =
106{
107	&default_lc,
108	&default_lc,
109	&default_lc,
110	&default_lc,
111	&default_lc,
112	&default_lc,
113	&default_lc,
114	&default_lc,
115	&default_lc,
116	&default_lc,
117	&default_lc,
118	&default_lc,
119	&default_lc,
120	&default_lc
121};
122
123/*
124 * return the internal category index for category
125 */
126
127int
128lcindex(int category, int min)
129{
130	switch (category)
131	{
132	case LC_ALL:		return min ? -1 : AST_LC_ALL;
133	case LC_ADDRESS:	return AST_LC_ADDRESS;
134	case LC_COLLATE:	return AST_LC_COLLATE;
135	case LC_CTYPE:		return AST_LC_CTYPE;
136	case LC_IDENTIFICATION:	return AST_LC_IDENTIFICATION;
137	case LC_LANG:		return AST_LC_LANG;
138	case LC_MEASUREMENT:	return AST_LC_MEASUREMENT;
139	case LC_MESSAGES:	return AST_LC_MESSAGES;
140	case LC_MONETARY:	return AST_LC_MONETARY;
141	case LC_NAME:		return AST_LC_NAME;
142	case LC_NUMERIC:	return AST_LC_NUMERIC;
143	case LC_PAPER:		return AST_LC_PAPER;
144	case LC_TELEPHONE:	return AST_LC_TELEPHONE;
145	case LC_TIME:		return AST_LC_TIME;
146	case LC_XLITERATE:	return AST_LC_XLITERATE;
147	}
148	return -1;
149}
150
151/*
152 * return the first category table entry
153 */
154
155Lc_category_t*
156lccategories(void)
157{
158	return (Lc_category_t*)&lc_categories[0];
159}
160
161/*
162 * return the current info for category
163 */
164
165Lc_info_t*
166lcinfo(register int category)
167{
168	if ((category = lcindex(category, 0)) < 0)
169		return 0;
170	return LCINFO(category);
171}
172
173/*
174 * return 1 if s matches the alternation pattern p
175 * if minimum!=0 then at least that many chars must match
176 * if standard!=0 and s[0] is a digit leading non-digits are ignored in p
177 */
178
179static int
180match(const char* s, register const char* p, int minimum, int standard)
181{
182	register const char*	t;
183	const char*		x;
184	int			w;
185	int			z;
186
187	z = 0;
188	do
189	{
190		t = s;
191		if (standard)
192		{
193			if (isdigit(*t))
194				while (*p && !isdigit(*p))
195					p++;
196			else if (isdigit(*p))
197				while (*t && !isdigit(*t))
198					t++;
199		}
200		if (*p)
201		{
202			w = 0;
203			x = p;
204			while (*p && *p != '|')
205			{
206				if (!*t || *t == ',')
207					break;
208				else if (*t == *p)
209					/*ok*/;
210				else if (*t == '-')
211				{
212					if (standard && isdigit(*p))
213					{
214						t++;
215						continue;
216					}
217					while (*p && *p != '-')
218						p++;
219					if (!*p)
220						break;
221				}
222				else if (*p == '-')
223				{
224					if (standard && isdigit(*t))
225					{
226						p++;
227						continue;
228					}
229					w = 1;
230					while (*t && *t != '-')
231						t++;
232					if (!*t)
233						break;
234				}
235				else
236					break;
237				t++;
238				p++;
239			}
240			if ((!*t || *t == ',') && (!*p || *p == '|' || w))
241				return p - x;
242			if (minimum && z < (p - x) && (p - x) >= minimum)
243				z = p - x;
244		}
245		while (*p && *p != '|')
246			p++;
247	} while (*p++);
248	return z;
249}
250
251/*
252 * return 1 if s matches the charset names in cp
253 */
254
255static int
256match_charset(register const char* s, register const Lc_charset_t* cp)
257{
258	return match(s, cp->code, 0, 1) || match(s, cp->alternates, 3, 1) || cp->ms && match(s, cp->ms, 0, 1);
259}
260
261/*
262 * low level for lccanon
263 */
264
265static size_t
266canonical(const Lc_language_t* lp, const Lc_territory_t* tp, const Lc_charset_t* cp, const Lc_attribute_list_t* ap, unsigned long flags, char* buf, size_t siz)
267{
268	register int		c;
269	register int		u;
270	register char*		s;
271	register char*		e;
272	register const char*	t;
273	char*			p;
274	char*			r;
275
276	if (!(flags & (LC_abbreviated|LC_default|LC_local|LC_qualified|LC_verbose)))
277		flags |= LC_abbreviated;
278	s = buf;
279	e = &buf[siz - 3];
280	if (lp)
281	{
282		if (lp->flags & (LC_debug|LC_default))
283		{
284			for (t = lp->code; s < e && (*s = *t++); s++);
285			*s++ = 0;
286			return s - buf;
287		}
288		if (flags & LC_verbose)
289		{
290			u = 1;
291			t = lp->name;
292			while (s < e && (c = *t++))
293			{
294				if (u)
295				{
296					u = 0;
297					c = toupper(c);
298				}
299				else if (!isalnum(c))
300					u = 1;
301				*s++ = c;
302			}
303		}
304		else
305			for (t = lp->code; s < e && (*s = *t++); s++);
306	}
307	if (s < e)
308	{
309		if (tp && tp != &lc_territories[0])
310		{
311			r = 0;
312			if (lp)
313			{
314				if ((flags & (LC_abbreviated|LC_default)) && streq(lp->code, tp->code))
315					r = s;
316				*s++ = '_';
317			}
318			if (flags & LC_verbose)
319			{
320				u = 1;
321				t = tp->name;
322				while (s < e && (c = *t++) && c != '|')
323				{
324					if (u)
325					{
326						u = 0;
327						c = toupper(c);
328					}
329					else if (!isalnum(c))
330						u = 1;
331					*s++ = c;
332				}
333			}
334			else
335				for (t = tp->code; s < e && (*s = toupper(*t++)); s++);
336			if (r)
337			{
338				*s = 0;
339				if ((p = setlocale(LC_MESSAGES, 0)) && (p = strdup(p)))
340				{
341					if (!setlocale(LC_MESSAGES, buf))
342					{
343						*r = 0;
344						if (!setlocale(LC_MESSAGES, buf))
345							*r = '_';
346					}
347					setlocale(LC_MESSAGES, p);
348					free(p);
349				}
350			}
351		}
352		if (lp && (!(flags & (LC_abbreviated|LC_default)) || cp != lp->charset) && s < e)
353		{
354			*s++ = '.';
355			t = cp->code;
356			if (streq(cp->code, "utf8") && (t = _locale_utf8_str))
357				for (; s < e && (c = *t++); s++)
358					*s = c;
359			else
360				for (t = cp->code; s < e && (c = *t++); s++)
361				{
362					if (islower(c))
363						c = toupper(c);
364					*s = c;
365				}
366		}
367		for (c = '@'; ap && s < e; ap = ap->next)
368			if (!(flags & (LC_abbreviated|LC_default|LC_verbose)) || !(ap->attribute->flags & LC_default))
369			{
370				*s++ = c;
371				c = ',';
372				for (t = ap->attribute->name; s < e && (*s = *t++); s++);
373			}
374	}
375	*s++ = 0;
376	return s - buf;
377}
378
379/*
380 * generate a canonical locale name in buf
381 */
382
383size_t
384lccanon(Lc_t* lc, unsigned long flags, char* buf, size_t siz)
385{
386	if ((flags & LC_local) && (!lc->language || !(lc->language->flags & (LC_debug|LC_default))))
387	{
388#if _WINIX
389		char	lang[64];
390		char	code[64];
391		char	ctry[64];
392
393		if (lc->index &&
394		    GetLocaleInfo(lc->index, LOCALE_SENGLANGUAGE, lang, sizeof(lang)) &&
395		    GetLocaleInfo(lc->index, LOCALE_SENGCOUNTRY, ctry, sizeof(ctry)))
396		{
397		    	if (!GetLocaleInfo(lc->index, LOCALE_IDEFAULTANSICODEPAGE, code, sizeof(code)))
398				code[0] = 0;
399			if (!lc->charset || !lc->charset->ms)
400				return sfsprintf(buf, siz, "%s_%s", lang, ctry);
401			else if (streq(lc->charset->ms, code))
402				return sfsprintf(buf, siz, "%s_%s.%s", lang, ctry, code);
403			else
404				return sfsprintf(buf, siz, "%s_%s.%s,%s", lang, ctry, code, lc->charset->ms);
405		}
406#endif
407		buf[0] = '-';
408		buf[1] = 0;
409		return 0;
410	}
411	return canonical(lc->language, lc->territory, lc->charset, lc->attributes, flags, buf, siz);
412}
413
414/*
415 * make an Lc_t from a locale name
416 */
417
418Lc_t*
419lcmake(const char* name)
420{
421	register int			c;
422	register char*			s;
423	register char*			e;
424	register const char*		t;
425	const char*			a;
426	char*				w;
427	char*				language_name;
428	char*				territory_name;
429	char*				charset_name;
430	char*				attributes_name;
431	Lc_t*				lc;
432	const Lc_map_t*			mp;
433	const Lc_language_t*		lp;
434	const Lc_territory_t*		tp;
435	const Lc_territory_t*		tpb;
436	const Lc_territory_t*		primary;
437	const Lc_charset_t*		cp;
438	const Lc_charset_t*		ppa;
439	const Lc_attribute_t*		ap;
440	Lc_attribute_list_t*		ai;
441	Lc_attribute_list_t*		al;
442	int				i;
443	int				n;
444	int				z;
445	char				buf[PATH_MAX / 2];
446	char				tmp[PATH_MAX / 2];
447	Local_t				local[2];
448
449	if (!(t = name) || !*t)
450		return &default_lc;
451	for (lc = lcs; lc; lc = lc->next)
452		if (!strcasecmp(t, lc->code) || !strcasecmp(t, lc->name))
453			return lc;
454	for (mp = lc_maps; mp->code; mp++)
455		if (streq(t, mp->code))
456		{
457			lp = mp->language;
458			tp = mp->territory;
459			cp = mp->charset;
460			if (!mp->attribute)
461				al = 0;
462			else if (al = newof(0, Lc_attribute_list_t, 1, 0))
463				al->attribute = mp->attribute;
464			goto mapped;
465		}
466	language_name = buf;
467	territory_name = charset_name = attributes_name = 0;
468	s = buf;
469	e = &buf[sizeof(buf)-2];
470	a = 0;
471	n = 0;
472	while (s < e && (c = *t++))
473	{
474		if (isspace(c) || (c == '(' || c == '-' && *t == '-') && ++n)
475		{
476			while ((c = *t++) && (isspace(c) || (c == '-' || c == '(' || c == ')') && ++n))
477			if (!c)
478				break;
479			if (isalnum(c) && !n)
480				*s++ = '-';
481			else
482			{
483				n = 0;
484				if (!a)
485				{
486					a = t - 1;
487					while (c && c != '_' && c != '.' && c != '@')
488						c = *t++;
489					if (!c)
490						break;
491				}
492			}
493		}
494		if (c == '_' && !territory_name)
495		{
496			*s++ = 0;
497			territory_name = s;
498		}
499		else if (c == '.' && !charset_name)
500		{
501			*s++ = 0;
502			charset_name = s;
503		}
504		else if (c == '@' && !attributes_name)
505		{
506			*s++ = 0;
507			attributes_name = s;
508		}
509		else
510		{
511			if (isupper(c))
512				c = tolower(c);
513			*s++ = c;
514		}
515	}
516	if ((t = a) && s < e)
517	{
518		if (attributes_name)
519			*s++ = ',';
520		else
521		{
522			*s++ = 0;
523			attributes_name = s;
524		}
525		while (s < e && (c = *t++))
526		{
527			if (isspace(c) || (c == '(' || c == ')' || c == '-' && *t == '-') && ++n)
528			{
529				while ((c = *t++) && (isspace(c) || (c == '-' || c == '(' || c == ')') && ++n))
530				if (!c)
531					break;
532				if (isalnum(c) && !n)
533					*s++ = '-';
534				else
535					n = 0;
536			}
537			if (c == '_' || c == '.' || c == '@')
538				break;
539			if (isupper(c))
540				c = tolower(c);
541			*s++ = c;
542		}
543	}
544	*s = 0;
545#if AHA
546	if ((ast.locale.set & AST_LC_debug) && !(ast.locale.set & AST_LC_internal))
547		sfprintf(sfstderr, "locale make %s language=%s territory=%s charset=%s attributes=%s\n", name, language_name, territory_name, charset_name, attributes_name);
548#endif
549	tp = 0;
550	cp = ppa = 0;
551	al = 0;
552
553	/*
554	 * language
555	 */
556
557	n = strlen(s = language_name);
558	if (n == 2)
559		for (lp = lc_languages; lp->code && !streq(s, lp->code); lp++);
560	else if (n == 3)
561	{
562		for (lp = lc_languages; lp->code && (!lp->alternates || !match(s, lp->alternates, n, 0)); lp++);
563		if (!lp->code)
564		{
565			c = s[2];
566			s[2] = 0;
567			for (lp = lc_languages; lp->code && !streq(s, lp->code); lp++);
568			s[2] = c;
569			if (lp->code)
570				n = 1;
571		}
572	}
573	else if (streq(s, "c") || streq(s, "posix"))
574		lp = &lc_languages[0];
575	else
576		lp = 0;
577	if (!lp || !lp->code)
578	{
579		for (lp = lc_languages; lp->code && !match(s, lp->name, 0, 0); lp++);
580		if (!lp || !lp->code)
581		{
582			if (!territory_name)
583			{
584				if (n == 2)
585					for (tp = lc_territories; tp->code && !streq(s, tp->code); tp++);
586				else
587				{
588					z = 0;
589					tpb = 0;
590					for (tp = lc_territories; tp->name; tp++)
591						if ((i = match(s, tp->name, 3, 0)) > z)
592						{
593							tpb = tp;
594							if ((z = i) == n)
595								break;
596						}
597					if (tpb)
598						tp = tpb;
599				}
600				if (tp->code)
601					lp = tp->languages[0];
602			}
603			if (!lp || !lp->code)
604			{
605				/*
606				 * name not in the tables so let
607				 * _ast_setlocale() and/or setlocale()
608				 * handle the validity checks
609				 */
610
611				s = (char*)name;
612				z = strlen(s) + 1;
613				if (!(lp = newof(0, Lc_language_t, 1, z)))
614					return 0;
615				name = ((Lc_language_t*)lp)->code = ((Lc_language_t*)lp)->name = (const char*)(lp + 1);
616				memcpy((char*)lp->code, s, z - 1);
617				tp = &lc_territories[0];
618				cp = &lc_charsets[0];
619				if (charset_name)
620					for (ppa = lc_charsets; ppa->code; ppa++)
621						if (match_charset(charset_name, ppa))
622						{
623							cp = ppa;
624							break;
625						}
626				((Lc_language_t*)lp)->charset = cp;
627				al = 0;
628				goto override;
629			}
630		}
631	}
632
633	/*
634	 * territory
635	 */
636
637	if (!tp || !tp->code)
638	{
639		if (!(s = territory_name))
640		{
641			n = 0;
642			primary = 0;
643			for (tp = lc_territories; tp->code; tp++)
644				if (tp->languages[0] == lp)
645				{
646					if (tp->flags & LC_primary)
647					{
648						n = 1;
649						primary = tp;
650						break;
651					}
652					n++;
653					primary = tp;
654				}
655			if (n == 1)
656				tp = primary;
657			s = (char*)lp->code;
658		}
659		if (!tp || !tp->code)
660		{
661			n = strlen(s);
662			if (n == 2)
663			{
664				for (tp = lc_territories; tp->code; tp++)
665					if (streq(s, tp->code))
666					{
667						if (lp != &lc_languages[0])
668						{
669							for (i = 0; i < elementsof(tp->languages) && lp != tp->languages[i]; i++);
670							if (i >= elementsof(tp->languages))
671								tp = 0;
672						}
673						break;
674					}
675			}
676			else
677			{
678				for (tp = lc_territories; tp->code; tp++)
679					if (match(s, tp->name, 3, 0))
680					{
681						for (i = 0; i < elementsof(tp->languages) && lp != tp->languages[i]; i++);
682						if (i < elementsof(tp->languages))
683							break;
684					}
685			}
686			if (tp && !tp->code)
687				tp = 0;
688		}
689	}
690
691	/*
692	 * attributes -- done here to catch misplaced charset references
693	 */
694
695	if (s = attributes_name)
696	{
697		do
698		{
699			for (w = s; *s && *s != ','; s++);
700			c = *s;
701			*s = 0;
702			if (!(cp = lp->charset) || !match_charset(w, cp))
703				for (cp = lc_charsets; cp->code; cp++)
704					if (match_charset(w, cp))
705					{
706						ppa = cp;
707						break;
708					}
709			if (!cp->code)
710			{
711				for (i = 0; i < elementsof(lp->attributes) && (ap = lp->attributes[i]); i++)
712					if (match(w, ap->name, 5, 0))
713					{
714						if (ai = newof(0, Lc_attribute_list_t, 1, 0))
715						{
716							ai->attribute = ap;
717							ai->next = al;
718							al = ai;
719						}
720						break;
721					}
722				if (i >= elementsof(lp->attributes) && (ap = newof(0, Lc_attribute_t, 1, sizeof(Lc_attribute_list_t) + s - w + 1)))
723				{
724					ai = (Lc_attribute_list_t*)(ap + 1);
725					strcpy((char*)(((Lc_attribute_t*)ap)->name = (const char*)(ai + 1)), w);
726					ai->attribute = ap;
727					ai->next = al;
728					al = ai;
729				}
730			}
731			*s = c;
732		} while (*s++);
733	}
734
735	/*
736	 * charset
737	 */
738
739	if (s = charset_name)
740		for (cp = lc_charsets; cp->code; cp++)
741			if (match_charset(s, cp))
742				break;
743#if AHA
744	if ((ast.locale.set & AST_LC_debug) && !(ast.locale.set & AST_LC_internal))
745		sfprintf(sfstderr, "locale make %s charset_name=%s cp=%s ppa=%s lp=%s\n", name, charset_name, cp ? cp->code : 0, ppa, lp->charset);
746#endif
747	if (!cp || !cp->code)
748		cp = ppa ? ppa : lp->charset;
749 mapped:
750	z = canonical(lp, tp, cp, al, 0, s = tmp, sizeof(tmp));
751
752	/*
753	 * add to the list of possibly active locales
754	 */
755
756 override:
757	n = strlen(name) + 1;
758	local[0].name = default_lc.name;
759	local[0].size = strlen(local[0].name);
760	local[1].name = default_lc.code;
761	local[1].size = strlen(local[1].name);
762	i = -1;
763	for (c = 0; c < elementsof(local); ++c)
764		if (strneq(name, local[c].name, local[c].size))
765		{
766			switch (name[local[c].size])
767			{
768			case '.':
769			case '_':
770			case 0:
771				i = c;
772				z += local[!i].size + n;
773				break;
774			}
775			break;
776		}
777	if (!(lc = newof(0, Lc_t, 1, n + z)))
778		return 0;
779	strcpy((char*)(lc->name = (const char*)(lc + 1)), name);
780	lc->code = lc->name + n;
781	if (i >= 0)
782	{
783		lc->flags |= LC_local;
784		strcpy((char*)lc->code, local[!i].name);
785		strcpy((char*)lc->code + local[!i].size, name + local[i].size);
786	}
787	else
788		strcpy((char*)lc->code, s);
789	lc->language = lp ? lp : &lc_languages[0];
790	lc->territory = tp ? tp : &lc_territories[0];
791	lc->charset = cp ? cp : &lc_charsets[0];
792	if (streq(lc->charset->code, "utf8"))
793		lc->flags |= LC_utf8;
794	lc->attributes = al;
795	for (i = 0; i < elementsof(lc->info); i++)
796		lc->info[i].lc = lc;
797#if _WINIX
798	n = SUBLANG_DEFAULT;
799	if (tp)
800		for (i = 0; i < elementsof(tp->languages); i++)
801			if (lp == tp->languages[i])
802			{
803				n = tp->indices[i];
804				break;
805			}
806	lc->index = MAKELCID(MAKELANGID(lp->index, n), SORT_DEFAULT);
807#endif
808	lc->next = lcs;
809	lcs = lc;
810	if ((ast.locale.set & AST_LC_debug) && !(ast.locale.set & AST_LC_internal))
811		sfprintf(sfstderr, "locale make %17s %16s %16s %16s language=%s territory=%s charset=%s%s\n", "", lc->name, lc->code, "", lc->language->name, lc->territory->name, lc->charset->code, (lc->flags & LC_local) ? " local" : "");
812	return lc;
813}
814
815/*
816 * return an Lc_t* for each locale in the tables
817 * one Lc_t is allocated on the first call with lc==0
818 * this is freed when 0 returned
819 * the return value is not part of the lcmake() cache
820 */
821
822typedef struct Lc_scan_s
823{
824	Lc_t			lc;
825	Lc_attribute_list_t	list;
826	int			territory;
827	int			language;
828	int			attribute;
829	char			buf[256];
830} Lc_scan_t;
831
832Lc_t*
833lcscan(Lc_t* lc)
834{
835	register Lc_scan_t*	ls;
836
837	if (!(ls = (Lc_scan_t*)lc))
838	{
839		if (!(ls = newof(0, Lc_scan_t, 1, 0)))
840			return 0;
841		ls->lc.code = ls->lc.name = ls->buf;
842		ls->territory = -1;
843		ls->language = elementsof(ls->lc.territory->languages);
844		ls->attribute = elementsof(ls->lc.language->attributes);
845	}
846	if (++ls->attribute >= elementsof(ls->lc.language->attributes) || !(ls->list.attribute = ls->lc.language->attributes[ls->attribute]))
847	{
848		if (++ls->language >= elementsof(ls->lc.territory->languages) || !(ls->lc.language = ls->lc.territory->languages[ls->language]))
849		{
850			if (!lc_territories[++ls->territory].code)
851			{
852				free(ls);
853				return 0;
854			}
855			ls->lc.territory = &lc_territories[ls->territory];
856			ls->lc.language = ls->lc.territory->languages[ls->language = 0];
857		}
858		if (ls->lc.language)
859		{
860			ls->lc.charset = ls->lc.language->charset ? ls->lc.language->charset : &lc_charsets[0];
861			ls->list.attribute = ls->lc.language->attributes[ls->attribute = 0];
862		}
863		else
864		{
865			ls->lc.charset = &lc_charsets[0];
866			ls->list.attribute = 0;
867		}
868	}
869	ls->lc.attributes = ls->list.attribute ? &ls->list : (Lc_attribute_list_t*)0;
870#if _WINIX
871	if (!ls->lc.language || !ls->lc.language->index)
872		ls->lc.index = 0;
873	else
874	{
875		if ((!ls->list.attribute || !(ls->lc.index = ls->list.attribute->index)) &&
876		    (!ls->lc.territory || !(ls->lc.index = ls->lc.territory->indices[ls->language])))
877			ls->lc.index = SUBLANG_DEFAULT;
878		ls->lc.index = MAKELCID(MAKELANGID(ls->lc.language->index, ls->lc.index), SORT_DEFAULT);
879	}
880#endif
881	canonical(ls->lc.language, ls->lc.territory, ls->lc.charset, ls->lc.attributes, 0, ls->buf, sizeof(ls->buf));
882	return (Lc_t*)ls;
883}
884