xref: /illumos-gate/usr/src/contrib/ast/src/lib/libast/port/lc.c (revision b30d193948be5a7794d7ae3ba0ed9c2f72c88e0f)
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 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
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 
34 typedef 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 
42 static Lc_numeric_t	default_numeric = { '.', -1 };
43 
44 static 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 
72 static Lc_numeric_t	debug_numeric = { ',', '.' };
73 
74 static 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 
103 static Lc_t*		lcs = &debug_lc;
104 
105 Lc_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 
127 int
lcindex(int category,int min)128 lcindex(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 
155 Lc_category_t*
lccategories(void)156 lccategories(void)
157 {
158 	return (Lc_category_t*)&lc_categories[0];
159 }
160 
161 /*
162  * return the current info for category
163  */
164 
165 Lc_info_t*
lcinfo(register int category)166 lcinfo(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 
179 static int
match(const char * s,register const char * p,int minimum,int standard)180 match(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 
255 static int
match_charset(register const char * s,register const Lc_charset_t * cp)256 match_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 
265 static size_t
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)266 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)
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 
383 size_t
lccanon(Lc_t * lc,unsigned long flags,char * buf,size_t siz)384 lccanon(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 
418 Lc_t*
lcmake(const char * name)419 lcmake(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 
822 typedef 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 
832 Lc_t*
lcscan(Lc_t * lc)833 lcscan(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