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